Merge "Disable dream touch handling on scene container" into main
diff --git a/ACTIVITY_MANAGER_OWNERS b/ACTIVITY_MANAGER_OWNERS
new file mode 100644
index 0000000..47782d1
--- /dev/null
+++ b/ACTIVITY_MANAGER_OWNERS
@@ -0,0 +1,4 @@
+mwachens@google.com
+sudheersai@google.com
+varunshah@google.com
+yamasani@google.com
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 0ca9789..a0f38d9 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -93,6 +93,7 @@
         "com.android.media.flags.performance-aconfig-java",
         "com.android.media.flags.projection-aconfig-java",
         "com.android.net.thread.platform.flags-aconfig-java",
+        "com.android.ranging.flags.ranging-aconfig-java",
         "com.android.server.contextualsearch.flags-java",
         "com.android.server.flags.services-aconfig-java",
         "com.android.text.flags-aconfig-java",
@@ -679,6 +680,11 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+cc_aconfig_library {
+    name: "com.android.media.flags.editing-aconfig-cc",
+    aconfig_declarations: "com.android.media.flags.editing-aconfig",
+}
+
 // MediaProjection
 aconfig_declarations {
     name: "com.android.media.flags.projection-aconfig",
@@ -1549,6 +1555,13 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// Ranging
+java_aconfig_library {
+    name: "com.android.ranging.flags.ranging-aconfig-java",
+    aconfig_declarations: "ranging_aconfig_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // System Server
 aconfig_declarations {
     name: "android.systemserver.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index 5b9f2cb..8c01585 100644
--- a/Android.bp
+++ b/Android.bp
@@ -99,6 +99,7 @@
         ":android.hardware.biometrics.common-V4-java-source",
         ":android.hardware.biometrics.fingerprint-V5-java-source",
         ":android.hardware.biometrics.fingerprint.virtualhal-java-source",
+        ":android.hardware.biometrics.face.virtualhal-java-source",
         ":android.hardware.biometrics.face-V4-java-source",
         ":android.hardware.gnss-V2-java-source",
         ":android.hardware.graphics.common-V3-java-source",
diff --git a/BATTERY_STATS_OWNERS b/BATTERY_STATS_OWNERS
index 7728975..575bded 100644
--- a/BATTERY_STATS_OWNERS
+++ b/BATTERY_STATS_OWNERS
@@ -1,4 +1,3 @@
 # OWNERS of BatteryStats related files
-bookatz@google.com
 dplotnikov@google.com
 mwachens@google.com
diff --git a/OOM_ADJUSTER_OWNERS b/OOM_ADJUSTER_OWNERS
new file mode 100644
index 0000000..7727f9f
--- /dev/null
+++ b/OOM_ADJUSTER_OWNERS
@@ -0,0 +1,3 @@
+mwachens@google.com
+dplotnikov@google.com
+tyk@google.com
diff --git a/PERFORMANCE_OWNERS b/PERFORMANCE_OWNERS
index 02b0a1e..d5d752f 100644
--- a/PERFORMANCE_OWNERS
+++ b/PERFORMANCE_OWNERS
@@ -7,3 +7,4 @@
 jdduke@google.com
 shombert@google.com
 kevinjeon@google.com
+yforta@google.com
diff --git a/apex/jobscheduler/ALARM_OWNERS b/apex/jobscheduler/ALARM_OWNERS
new file mode 100644
index 0000000..5c3bff7
--- /dev/null
+++ b/apex/jobscheduler/ALARM_OWNERS
@@ -0,0 +1,2 @@
+suprabh@google.com
+tetianameronyk@google.com
\ No newline at end of file
diff --git a/apex/jobscheduler/DEVICE_IDLE_OWNERS b/apex/jobscheduler/DEVICE_IDLE_OWNERS
new file mode 100644
index 0000000..62db91d
--- /dev/null
+++ b/apex/jobscheduler/DEVICE_IDLE_OWNERS
@@ -0,0 +1,2 @@
+suprabh@google.com
+guanxin@google.com
\ No newline at end of file
diff --git a/apex/jobscheduler/JOB_OWNERS b/apex/jobscheduler/JOB_OWNERS
new file mode 100644
index 0000000..05bf25c
--- /dev/null
+++ b/apex/jobscheduler/JOB_OWNERS
@@ -0,0 +1,3 @@
+suprabh@google.com
+varunshah@google.com
+guanxin@google.com
\ No newline at end of file
diff --git a/apex/jobscheduler/OWNERS b/apex/jobscheduler/OWNERS
index 22b6489..ffa5802 100644
--- a/apex/jobscheduler/OWNERS
+++ b/apex/jobscheduler/OWNERS
@@ -1,8 +1,25 @@
-ctate@android.com
-ctate@google.com
-dplotnikov@google.com
-jji@google.com
-omakoto@google.com
-suprabh@google.com
-varunshah@google.com
-yamasani@google.com
+# For Job Scheduler and App Standby changes, JOB_OWNERS
+per-file JOB_OWNERS = file:/apex/jobscheduler/JOB_OWNERS
+per-file framework/aconfig/job.aconfig = file:/apex/jobscheduler/JOB_OWNERS
+per-file service/aconfig/job.aconfig = file:/apex/jobscheduler/JOB_OWNERS
+per-file service/aconfig/app_idle.aconfig = file:/apex/jobscheduler/JOB_OWNERS
+per-file *Job* = file:/apex/jobscheduler/JOB_OWNERS
+per-file *Standby* = file:/apex/jobscheduler/JOB_OWNERS
+per-file framework/java/android/app/job/* = file:/apex/jobscheduler/JOB_OWNERS
+per-file framework/java/com/android/server/job/* = file:/apex/jobscheduler/JOB_OWNERS
+per-file service/java/com/android/server/job/* = file:/apex/jobscheduler/JOB_OWNERS
+
+# For Alarm Manager changes, ALARM_OWNERS
+per-file ALARM_OWNERS = file:/apex/jobscheduler/ALARM_OWNERS
+per-file service/aconfig/alarm.aconfig = file:/apex/jobscheduler/ALARM_OWNERS
+per-file *Alarm* = file:/apex/jobscheduler/ALARM_OWNERS
+per-file service/java/com/android/server/alarm/* = file:/apex/jobscheduler/ALARM_OWNERS
+
+# For Device Idle changes, DEVICE_IDLE_OWNERS
+per-file DEVICE_IDLE_OWNERS = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
+per-file service/aconfig/device_idle.aconfig = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
+per-file *Idle* = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
+per-file service/java/com/android/server/deviceidle/* = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
+per-file framework/java/com/android/server/deviceidle/* = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS
+
+suprabh@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/apex/jobscheduler/framework/java/android/app/job/OWNERS b/apex/jobscheduler/framework/java/android/app/job/OWNERS
deleted file mode 100644
index 0b1e559..0000000
--- a/apex/jobscheduler/framework/java/android/app/job/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 330738
-
-yamasani@google.com
-omakoto@google.com
-ctate@android.com
-ctate@google.com
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 0f3b1c3..033da2d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -4944,10 +4944,14 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
             final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
             synchronized (mLock) {
                 String pkgList[] = null;
-                switch (intent.getAction()) {
+                switch (action) {
                     case Intent.ACTION_QUERY_PACKAGE_RESTART:
                         pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
                         for (String packageName : pkgList) {
diff --git a/api/Android.bp b/api/Android.bp
index 533f9f6..3f2316f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -102,6 +102,11 @@
             "framework-crashrecovery",
         ],
         default: [],
+    }) + select(release_flag("RELEASE_RANGING_STACK"), {
+        true: [
+            "framework-ranging",
+        ],
+        default: [],
     }),
     system_server_classpath: [
         "service-art",
diff --git a/core/api/current.txt b/core/api/current.txt
index 542543d..836d4e99 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5333,6 +5333,7 @@
     method @NonNull public String getProcessName();
     method public int getRealUid();
     method public int getReason();
+    method @FlaggedApi("android.app.app_start_info_component") public int getStartComponent();
     method public int getStartType();
     method public int getStartupState();
     method @NonNull public java.util.Map<java.lang.Integer,java.lang.Long> getStartupTimestamps();
@@ -5347,6 +5348,11 @@
     field public static final int STARTUP_STATE_ERROR = 1; // 0x1
     field public static final int STARTUP_STATE_FIRST_FRAME_DRAWN = 2; // 0x2
     field public static final int STARTUP_STATE_STARTED = 0; // 0x0
+    field @FlaggedApi("android.app.app_start_info_component") public static final int START_COMPONENT_ACTIVITY = 1; // 0x1
+    field @FlaggedApi("android.app.app_start_info_component") public static final int START_COMPONENT_BROADCAST = 2; // 0x2
+    field @FlaggedApi("android.app.app_start_info_component") public static final int START_COMPONENT_CONTENT_PROVIDER = 3; // 0x3
+    field @FlaggedApi("android.app.app_start_info_component") public static final int START_COMPONENT_OTHER = 5; // 0x5
+    field @FlaggedApi("android.app.app_start_info_component") public static final int START_COMPONENT_SERVICE = 4; // 0x4
     field public static final int START_REASON_ALARM = 0; // 0x0
     field public static final int START_REASON_BACKUP = 1; // 0x1
     field public static final int START_REASON_BOOT_COMPLETE = 2; // 0x2
@@ -6495,6 +6501,7 @@
     field public static final int FLAG_NO_CLEAR = 32; // 0x20
     field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
     field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field @FlaggedApi("android.app.api_rich_ongoing") public static final int FLAG_PROMOTED_ONGOING = 262144; // 0x40000
     field @Deprecated public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
     field public static final int FOREGROUND_SERVICE_DEFAULT = 0; // 0x0
     field public static final int FOREGROUND_SERVICE_DEFERRED = 2; // 0x2
@@ -6847,6 +6854,47 @@
     method public android.app.Notification.MessagingStyle.Message setData(String, android.net.Uri);
   }
 
+  @FlaggedApi("android.app.api_rich_ongoing") public static class Notification.ProgressStyle extends android.app.Notification.Style {
+    ctor public Notification.ProgressStyle();
+    method @NonNull public android.app.Notification.ProgressStyle addProgressSegment(@NonNull android.app.Notification.ProgressStyle.Segment);
+    method @NonNull public android.app.Notification.ProgressStyle addProgressStep(@NonNull android.app.Notification.ProgressStyle.Step);
+    method public int getProgress();
+    method @Nullable public android.graphics.drawable.Icon getProgressEndIcon();
+    method public int getProgressMax();
+    method @NonNull public java.util.List<android.app.Notification.ProgressStyle.Segment> getProgressSegments();
+    method @Nullable public android.graphics.drawable.Icon getProgressStartIcon();
+    method @NonNull public java.util.List<android.app.Notification.ProgressStyle.Step> getProgressSteps();
+    method @Nullable public android.graphics.drawable.Icon getProgressTrackerIcon();
+    method public boolean isProgressIndeterminate();
+    method public boolean isStyledByProgress();
+    method @NonNull public android.app.Notification.ProgressStyle setProgress(int);
+    method @NonNull public android.app.Notification.ProgressStyle setProgressEndIcon(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.app.Notification.ProgressStyle setProgressIndeterminate(boolean);
+    method @NonNull public android.app.Notification.ProgressStyle setProgressSegments(@NonNull java.util.List<android.app.Notification.ProgressStyle.Segment>);
+    method @NonNull public android.app.Notification.ProgressStyle setProgressStartIcon(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.app.Notification.ProgressStyle setProgressSteps(@NonNull java.util.List<android.app.Notification.ProgressStyle.Step>);
+    method @NonNull public android.app.Notification.ProgressStyle setProgressTrackerIcon(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.app.Notification.ProgressStyle setStyledByProgress(boolean);
+  }
+
+  public static final class Notification.ProgressStyle.Segment {
+    ctor public Notification.ProgressStyle.Segment(int);
+    method @ColorInt public int getColor();
+    method public int getLength();
+    method public int getStableId();
+    method @NonNull public android.app.Notification.ProgressStyle.Segment setColor(@ColorInt int);
+    method @NonNull public android.app.Notification.ProgressStyle.Segment setStableId(int);
+  }
+
+  public static final class Notification.ProgressStyle.Step {
+    ctor public Notification.ProgressStyle.Step(int);
+    method @ColorInt public int getColor();
+    method public int getPosition();
+    method public int getStableId();
+    method @NonNull public android.app.Notification.ProgressStyle.Step setColor(@ColorInt int);
+    method @NonNull public android.app.Notification.ProgressStyle.Step setStableId(int);
+  }
+
   public abstract static class Notification.Style {
     ctor @Deprecated public Notification.Style();
     method public android.app.Notification build();
@@ -7008,6 +7056,7 @@
     method public boolean areNotificationsEnabled();
     method public boolean areNotificationsPaused();
     method public boolean canNotifyAsPackage(@NonNull String);
+    method @FlaggedApi("android.app.api_rich_ongoing") public boolean canPostPromotedNotifications();
     method public boolean canUseFullScreenIntent();
     method public void cancel(int);
     method public void cancel(@Nullable String, int);
@@ -8724,13 +8773,15 @@
 package android.app.appfunctions {
 
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
-    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+    method @Deprecated @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
   }
 
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+    method @Deprecated @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+    method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
 
@@ -19396,7 +19447,7 @@
   public abstract static class CameraExtensionSession.ExtensionCaptureCallback {
     ctor public CameraExtensionSession.ExtensionCaptureCallback();
     method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, int);
+    method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, int);
     method public void onCaptureProcessProgressed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @IntRange(from=0, to=100) int);
     method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
     method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult);
@@ -19921,7 +19972,7 @@
     field @Deprecated @NonNull public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.String> LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID;
-    field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION;
+    field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
@@ -19947,7 +19998,7 @@
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_FACE_DETECT_MODE;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Point[]> STATISTICS_HOT_PIXEL_MAP;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
-    field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.LensIntrinsicsSample[]> STATISTICS_LENS_INTRINSICS_SAMPLES;
+    field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.LensIntrinsicsSample[]> STATISTICS_LENS_INTRINSICS_SAMPLES;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE;
@@ -20107,10 +20158,10 @@
     method public boolean isMultiResolution();
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class LensIntrinsicsSample {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public LensIntrinsicsSample(long, @NonNull float[]);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public float[] getLensIntrinsics();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getTimestampNanos();
+  public final class LensIntrinsicsSample {
+    ctor public LensIntrinsicsSample(long, @NonNull float[]);
+    method @NonNull public float[] getLensIntrinsics();
+    method public long getTimestampNanos();
   }
 
   public final class LensShadingMap {
@@ -36934,14 +36985,16 @@
 
   @FlaggedApi("android.provider.new_default_account_api_enabled") public static final class ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState {
     ctor public ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState(int, @Nullable android.accounts.Account);
-    method @Nullable public android.accounts.Account getCloudAccount();
+    method @Nullable public android.accounts.Account getAccount();
     method public int getState();
     method @NonNull public static android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState ofCloud(@NonNull android.accounts.Account);
     method @NonNull public static android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState ofLocal();
     method @NonNull public static android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState ofNotSet();
+    method @NonNull public static android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState ofSim(@NonNull android.accounts.Account);
     field public static final int DEFAULT_ACCOUNT_STATE_CLOUD = 3; // 0x3
     field public static final int DEFAULT_ACCOUNT_STATE_LOCAL = 2; // 0x2
     field public static final int DEFAULT_ACCOUNT_STATE_NOT_SET = 1; // 0x1
+    field public static final int DEFAULT_ACCOUNT_STATE_SIM = 4; // 0x4
   }
 
   public static final class ContactsContract.RawContacts.DisplayPhoto {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7a8e829..a1561c2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3835,6 +3835,7 @@
     field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String ON_DEVICE_INTELLIGENCE_SERVICE = "on_device_intelligence";
     field public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
     field public static final String PERMISSION_SERVICE = "permission";
+    field @FlaggedApi("com.android.ranging.flags.ranging_stack_enabled") public static final String RANGING_SERVICE = "ranging";
     field public static final String REBOOT_READINESS_SERVICE = "reboot_readiness";
     field public static final String ROLLBACK_SERVICE = "rollback";
     field public static final String SAFETY_CENTER_SERVICE = "safety_center";
@@ -4949,94 +4950,94 @@
 
 package android.hardware.camera2.extension {
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract class AdvancedExtender {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public AdvancedExtender(@NonNull android.hardware.camera2.CameraManager);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.List<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(@NonNull String);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.List<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(@NonNull String);
-    method @FlaggedApi("com.android.internal.camera.flags.camera_extensions_characteristics_get") @NonNull public abstract java.util.List<android.util.Pair<android.hardware.camera2.CameraCharacteristics.Key,java.lang.Object>> getAvailableCharacteristicsKeyValues();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getMetadataVendorId(@NonNull String);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract android.hardware.camera2.extension.SessionProcessor getSessionProcessor();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.Map<java.lang.Integer,java.util.List<android.util.Size>> getSupportedCaptureOutputResolutions(@NonNull String);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.Map<java.lang.Integer,java.util.List<android.util.Size>> getSupportedPreviewOutputResolutions(@NonNull String);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void initialize(@NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract boolean isExtensionAvailable(@NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap);
+  public abstract class AdvancedExtender {
+    ctor public AdvancedExtender(@NonNull android.hardware.camera2.CameraManager);
+    method @NonNull public abstract java.util.List<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(@NonNull String);
+    method @NonNull public abstract java.util.List<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(@NonNull String);
+    method @NonNull public abstract java.util.List<android.util.Pair<android.hardware.camera2.CameraCharacteristics.Key,java.lang.Object>> getAvailableCharacteristicsKeyValues();
+    method public long getMetadataVendorId(@NonNull String);
+    method @NonNull public abstract android.hardware.camera2.extension.SessionProcessor getSessionProcessor();
+    method @NonNull public abstract java.util.Map<java.lang.Integer,java.util.List<android.util.Size>> getSupportedCaptureOutputResolutions(@NonNull String);
+    method @NonNull public abstract java.util.Map<java.lang.Integer,java.util.List<android.util.Size>> getSupportedPreviewOutputResolutions(@NonNull String);
+    method public abstract void initialize(@NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap);
+    method public abstract boolean isExtensionAvailable(@NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap);
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract class CameraExtensionService extends android.app.Service {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") protected CameraExtensionService();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract android.hardware.camera2.extension.AdvancedExtender onInitializeAdvancedExtension(int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract boolean onRegisterClient(@NonNull android.os.IBinder);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void onUnregisterClient(@NonNull android.os.IBinder);
+  public abstract class CameraExtensionService extends android.app.Service {
+    ctor protected CameraExtensionService();
+    method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @NonNull public abstract android.hardware.camera2.extension.AdvancedExtender onInitializeAdvancedExtension(int);
+    method public abstract boolean onRegisterClient(@NonNull android.os.IBinder);
+    method public abstract void onUnregisterClient(@NonNull android.os.IBinder);
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class CameraOutputSurface {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public CameraOutputSurface(@NonNull android.view.Surface, @NonNull android.util.Size);
+  public final class CameraOutputSurface {
+    ctor public CameraOutputSurface(@NonNull android.view.Surface, @NonNull android.util.Size);
     method public int getColorSpace();
     method public long getDynamicRangeProfile();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int getImageFormat();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public android.util.Size getSize();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public android.view.Surface getSurface();
+    method public int getImageFormat();
+    method @NonNull public android.util.Size getSize();
+    method @NonNull public android.view.Surface getSurface();
     method public void setDynamicRangeProfile(long);
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public class CharacteristicsMap {
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @Nullable public android.hardware.camera2.CameraCharacteristics get(@NonNull String);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public java.util.Set<java.lang.String> getCameraIds();
+  public class CharacteristicsMap {
+    method @Nullable public android.hardware.camera2.CameraCharacteristics get(@NonNull String);
+    method @NonNull public java.util.Set<java.lang.String> getCameraIds();
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public class ExtensionConfiguration {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public ExtensionConfiguration(int, int, @NonNull java.util.List<android.hardware.camera2.extension.ExtensionOutputConfiguration>, @Nullable android.hardware.camera2.CaptureRequest);
+  public class ExtensionConfiguration {
+    ctor public ExtensionConfiguration(int, int, @NonNull java.util.List<android.hardware.camera2.extension.ExtensionOutputConfiguration>, @Nullable android.hardware.camera2.CaptureRequest);
     method public void setColorSpace(int);
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public class ExtensionOutputConfiguration {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public ExtensionOutputConfiguration(@NonNull java.util.List<android.hardware.camera2.extension.CameraOutputSurface>, int, @Nullable String, int);
+  public class ExtensionOutputConfiguration {
+    ctor public ExtensionOutputConfiguration(@NonNull java.util.List<android.hardware.camera2.extension.CameraOutputSurface>, int, @Nullable String, int);
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class RequestProcessor {
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void abortCaptures();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int setRepeating(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException;
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void stopRepeating();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int submit(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException;
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int submitBurst(@NonNull java.util.List<android.hardware.camera2.extension.RequestProcessor.Request>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException;
+  public final class RequestProcessor {
+    method public void abortCaptures();
+    method public int setRepeating(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException;
+    method public void stopRepeating();
+    method public int submit(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException;
+    method public int submitBurst(@NonNull java.util.List<android.hardware.camera2.extension.RequestProcessor.Request>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.RequestProcessor.RequestCallback) throws android.hardware.camera2.CameraAccessException;
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final class RequestProcessor.Request {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public RequestProcessor.Request(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>>, int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>> getParameters();
+  public static final class RequestProcessor.Request {
+    ctor public RequestProcessor.Request(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>>, int);
+    method @NonNull public java.util.List<android.util.Pair<android.hardware.camera2.CaptureRequest.Key,java.lang.Object>> getParameters();
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface RequestProcessor.RequestCallback {
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureBufferLost(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, long, int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @Nullable android.hardware.camera2.TotalCaptureResult);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull android.hardware.camera2.CaptureFailure);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureProgressed(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull android.hardware.camera2.CaptureResult);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceAborted(int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceCompleted(int, long);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureStarted(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, long, long);
+  public static interface RequestProcessor.RequestCallback {
+    method public void onCaptureBufferLost(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, long, int);
+    method public void onCaptureCompleted(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @Nullable android.hardware.camera2.TotalCaptureResult);
+    method public void onCaptureFailed(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull android.hardware.camera2.CaptureFailure);
+    method public void onCaptureProgressed(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, @NonNull android.hardware.camera2.CaptureResult);
+    method public void onCaptureSequenceAborted(int);
+    method public void onCaptureSequenceCompleted(int, long);
+    method public void onCaptureStarted(@NonNull android.hardware.camera2.extension.RequestProcessor.Request, long, long);
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract class SessionProcessor {
-    ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public SessionProcessor();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void deInitSession(@NonNull android.os.IBinder);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract android.hardware.camera2.extension.ExtensionConfiguration initSession(@NonNull android.os.IBinder, @NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap, @NonNull android.hardware.camera2.extension.CameraOutputSurface, @NonNull android.hardware.camera2.extension.CameraOutputSurface);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void onCaptureSessionEnd();
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void onCaptureSessionStart(@NonNull android.hardware.camera2.extension.RequestProcessor, @NonNull String);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void setParameters(@NonNull android.hardware.camera2.CaptureRequest);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startMultiFrameCapture(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startRepeating(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract int startTrigger(@NonNull android.hardware.camera2.CaptureRequest, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public abstract void stopRepeating();
+  public abstract class SessionProcessor {
+    ctor public SessionProcessor();
+    method public abstract void deInitSession(@NonNull android.os.IBinder);
+    method @NonNull public abstract android.hardware.camera2.extension.ExtensionConfiguration initSession(@NonNull android.os.IBinder, @NonNull String, @NonNull android.hardware.camera2.extension.CharacteristicsMap, @NonNull android.hardware.camera2.extension.CameraOutputSurface, @NonNull android.hardware.camera2.extension.CameraOutputSurface);
+    method public abstract void onCaptureSessionEnd();
+    method public abstract void onCaptureSessionStart(@NonNull android.hardware.camera2.extension.RequestProcessor, @NonNull String);
+    method public abstract void setParameters(@NonNull android.hardware.camera2.CaptureRequest);
+    method public abstract int startMultiFrameCapture(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback);
+    method public abstract int startRepeating(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback);
+    method public abstract int startTrigger(@NonNull android.hardware.camera2.CaptureRequest, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.extension.SessionProcessor.CaptureCallback);
+    method public abstract void stopRepeating();
   }
 
-  @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface SessionProcessor.CaptureCallback {
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull java.util.Map<android.hardware.camera2.CaptureResult.Key,java.lang.Object>);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(int, int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureProcessStarted(int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceAborted(int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceCompleted(int);
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureStarted(int, long);
+  public static interface SessionProcessor.CaptureCallback {
+    method public void onCaptureCompleted(long, int, @NonNull java.util.Map<android.hardware.camera2.CaptureResult.Key,java.lang.Object>);
+    method public void onCaptureFailed(int, int);
+    method public void onCaptureProcessStarted(int);
+    method public void onCaptureSequenceAborted(int);
+    method public void onCaptureSequenceCompleted(int);
+    method public void onCaptureStarted(int, long);
   }
 
 }
@@ -6821,6 +6822,27 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR;
   }
 
+  @FlaggedApi("android.media.soundtrigger.manager_api") public static final class SoundTrigger.RecognitionConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAudioCapabilities();
+    method @NonNull public byte[] getData();
+    method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> getKeyphrases();
+    method public boolean isAllowMultipleTriggers();
+    method public boolean isCaptureRequested();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.RecognitionConfig> CREATOR;
+  }
+
+  public static final class SoundTrigger.RecognitionConfig.Builder {
+    ctor public SoundTrigger.RecognitionConfig.Builder();
+    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig build();
+    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAllowMultipleTriggers(boolean);
+    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAudioCapabilities(int);
+    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setCaptureRequested(boolean);
+    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setData(@Nullable byte[]);
+    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setKeyphrases(@NonNull java.util.Collection<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
+  }
+
   public static class SoundTrigger.RecognitionEvent {
     method @Nullable public android.media.AudioFormat getCaptureFormat();
     method public int getCaptureSession();
@@ -7771,10 +7793,16 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void deleteModel(java.util.UUID);
     method public int getDetectionServiceOperationsTimeout();
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getModelState(@NonNull java.util.UUID);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModuleProperties getModuleProperties();
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public boolean isRecognitionActive(@NonNull java.util.UUID);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int loadSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.SoundModel);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModelParamRange queryParameter(@Nullable java.util.UUID, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int startRecognition(@NonNull java.util.UUID, @Nullable android.os.Bundle, @NonNull android.content.ComponentName, @NonNull android.hardware.soundtrigger.SoundTrigger.RecognitionConfig);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int stopRecognition(@NonNull java.util.UUID);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int unloadSoundModel(@NonNull java.util.UUID);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
   }
 
@@ -13019,6 +13047,8 @@
     method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
     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.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";
     field public static final int SOURCE_FROM_APP = 0; // 0x0
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 72a68f8..0a10920 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -398,6 +398,7 @@
     method public android.content.ComponentName getEffectsSuppressor();
     method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
     method @FlaggedApi("android.app.modes_api") public boolean removeAutomaticZenRule(@NonNull String, boolean);
+    method @FlaggedApi("android.app.api_rich_ongoing") public void setCanPostPromotedNotifications(@NonNull String, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean);
     method @FlaggedApi("android.app.modes_api") public boolean updateAutomaticZenRule(@NonNull String, @NonNull android.app.AutomaticZenRule, boolean);
@@ -1613,15 +1614,15 @@
   public final class CameraManager {
     method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String, boolean) throws android.hardware.camera2.CameraAccessException;
     method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException;
-    method @FlaggedApi("com.android.window.flags.camera_compat_for_freeform") public static int getRotationOverrideInternal(@Nullable android.content.Context, @Nullable android.content.pm.PackageManager, @Nullable String);
+    method @FlaggedApi("com.android.window.flags.enable_camera_compat_for_desktop_windowing") public static int getRotationOverrideInternal(@Nullable android.content.Context, @Nullable android.content.pm.PackageManager, @Nullable String);
     method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, boolean, @Nullable android.os.Handler, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
     method @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openCamera(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
     method public static boolean shouldOverrideToPortrait(@Nullable android.content.pm.PackageManager, @Nullable String);
     field public static final String LANDSCAPE_TO_PORTRAIT_PROP = "camera.enable_landscape_to_portrait";
     field public static final long OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT = 250678880L; // 0xef10e60L
-    field @FlaggedApi("com.android.window.flags.camera_compat_for_freeform") public static final int ROTATION_OVERRIDE_NONE = 0; // 0x0
-    field @FlaggedApi("com.android.window.flags.camera_compat_for_freeform") public static final int ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT = 1; // 0x1
-    field @FlaggedApi("com.android.window.flags.camera_compat_for_freeform") public static final int ROTATION_OVERRIDE_ROTATION_ONLY = 2; // 0x2
+    field @FlaggedApi("com.android.window.flags.enable_camera_compat_for_desktop_windowing") public static final int ROTATION_OVERRIDE_NONE = 0; // 0x0
+    field @FlaggedApi("com.android.window.flags.enable_camera_compat_for_desktop_windowing") public static final int ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT = 1; // 0x1
+    field @FlaggedApi("com.android.window.flags.enable_camera_compat_for_desktop_windowing") public static final int ROTATION_OVERRIDE_ROTATION_ONLY = 2; // 0x2
   }
 
   public abstract static class CameraManager.AvailabilityCallback {
@@ -1885,17 +1886,9 @@
     ctor public SoundTrigger.ModuleProperties(int, @NonNull String, @NonNull String, @NonNull String, int, @NonNull String, int, int, int, int, boolean, int, boolean, int, boolean, int);
   }
 
-  public static final class SoundTrigger.RecognitionConfig implements android.os.Parcelable {
-    ctor public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[], int);
+  @FlaggedApi("android.media.soundtrigger.manager_api") public static final class SoundTrigger.RecognitionConfig implements android.os.Parcelable {
+    ctor @Deprecated public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[], int);
     ctor public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[]);
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.RecognitionConfig> CREATOR;
-    field public final boolean allowMultipleTriggers;
-    field public final int audioCapabilities;
-    field public final boolean captureRequested;
-    field @NonNull public final byte[] data;
-    field @NonNull public final android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[] keyphrases;
   }
 
   public static class SoundTrigger.RecognitionEvent {
@@ -2216,7 +2209,7 @@
   public class SoundTriggerInstrumentation.RecognitionSession {
     method public void clearRecognitionCallback();
     method public int getAudioSession();
-    method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig getRecognitionConfig();
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig getRecognitionConfig();
     method public void setRecognitionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.soundtrigger.SoundTriggerInstrumentation.RecognitionCallback);
     method public void triggerAbortRecognition();
     method public void triggerRecognitionEvent(@NonNull byte[], @Nullable java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
@@ -2227,7 +2220,6 @@
     method @NonNull public android.media.soundtrigger.SoundTriggerManager createManagerForModule(@NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties);
     method @NonNull public android.media.soundtrigger.SoundTriggerManager createManagerForTestModule();
     method @NonNull public static java.util.List<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> listModuleProperties();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int loadSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.SoundModel);
   }
 
   public static class SoundTriggerManager.Model {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5db79fe..5b556cc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -230,6 +230,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.ApplicationSharedMemory;
 import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.DebugStore;
@@ -255,6 +256,7 @@
 import libcore.io.IoUtils;
 import libcore.io.Os;
 import libcore.net.event.NetworkEventDispatcher;
+import libcore.util.NativeAllocationRegistry;
 
 import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
 
@@ -1300,6 +1302,7 @@
                 long[] disabledCompatChanges,
                 long[] loggableCompatChanges,
                 SharedMemory serializedSystemFontMap,
+                FileDescriptor applicationSharedMemoryFd,
                 long startRequestedElapsedTime,
                 long startRequestedUptime) {
             if (services != null) {
@@ -1328,6 +1331,16 @@
                 ServiceManager.initServiceCache(services);
             }
 
+            // This must be initialized as early as possible to ensure availability for any
+            // downstream callers.
+            if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()) {
+                ApplicationSharedMemory instance =
+                        ApplicationSharedMemory.fromFileDescriptor(
+                                applicationSharedMemoryFd, /* mutable= */ false);
+                instance.closeFileDescriptor();
+                ApplicationSharedMemory.setInstance(instance);
+            }
+
             setCoreSettings(coreSettings);
 
             AppBindData data = new AppBindData();
@@ -1610,6 +1623,32 @@
         }
 
         @NeverCompile
+        private void dumpMemInfoNativeAllocations(PrintWriter pw) {
+            pw.println(" ");
+            pw.println(" Native Allocations");
+            printRow(pw, TWO_COUNT_COLUMN_HEADER, "", "Count", "", "Total(kB)");
+            printRow(pw, TWO_COUNT_COLUMN_HEADER, "", "------", "", "------");
+
+            for (NativeAllocationRegistry.Metrics m : NativeAllocationRegistry.getMetrics()) {
+                // group into 3 major categories: Bitmap, HardwareBuffer and Other
+                final String className = switch (m.getClassName()) {
+                    case "android.graphics.Bitmap" -> "Bitmap";
+                    case "android.hardware.HardwareBuffer" -> "HardwareBuffer";
+                    default -> "Other";
+                };
+
+                if (m.getMallocedCount() != 0 || m.getMallocedBytes() != 0) {
+                    printRow(pw, TWO_COUNT_COLUMNS, className + " (malloced):",
+                        m.getMallocedCount(), "", m.getMallocedBytes() / 1024);
+                }
+                if (m.getNonmallocedCount() != 0 || m.getNonmallocedBytes() != 0) {
+                    printRow(pw, TWO_COUNT_COLUMNS, className + " (nonmalloced):",
+                        m.getNonmallocedCount(), "", m.getNonmallocedBytes() / 1024);
+                }
+            }
+        }
+
+        @NeverCompile
         private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
                 boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
                 boolean dumpUnreachable, boolean dumpAllocatorStats) {
@@ -1707,6 +1746,10 @@
             printRow(pw, TWO_COUNT_COLUMNS, "Death Recipients:", binderDeathObjectCount,
                     "WebViews:", webviewInstanceCount);
 
+            if (com.android.libcore.Flags.nativeMetrics()) {
+                dumpMemInfoNativeAllocations(pw);
+            }
+
             // SQLite mem info
             pw.println(" ");
             pw.println(" SQL");
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 1e9a79b..edcdb6c 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -102,10 +102,10 @@
     /** Process started due to boot complete. */
     public static final int START_REASON_BOOT_COMPLETE = 2;
 
-    /**  Process started due to broadcast received. */
+    /**  Process started due to broadcast received for any reason not explicitly listed. */
     public static final int START_REASON_BROADCAST = 3;
 
-    /** Process started due to access of ContentProvider */
+    /** Process started due to access of ContentProvider for any reason not explicitly listed. */
     public static final int START_REASON_CONTENT_PROVIDER = 4;
 
     /** * Process started to run scheduled job. */
@@ -123,7 +123,7 @@
     /** Process started due to push message. */
     public static final int START_REASON_PUSH = 9;
 
-    /** Process service started. */
+    /** Process started due to Service started for any reason not explicitly listed.. */
     public static final int START_REASON_SERVICE = 10;
 
     /** Process started due to Activity started for any reason not explicitly listed. */
@@ -209,6 +209,26 @@
     /** Clock monotonic timestamp of surfaceflinger composition complete. */
     public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7;
 
+    /** Process was started for an activity component. */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO_COMPONENT)
+    public static final int START_COMPONENT_ACTIVITY = 1;
+
+    /** Process was started for a broadcast component. */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO_COMPONENT)
+    public static final int START_COMPONENT_BROADCAST = 2;
+
+    /** Process was started for a content provider component. */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO_COMPONENT)
+    public static final int START_COMPONENT_CONTENT_PROVIDER = 3;
+
+    /** Process was started for a service component. */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO_COMPONENT)
+    public static final int START_COMPONENT_SERVICE = 4;
+
+    /** Process was started not for one of the four standard components. */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO_COMPONENT)
+    public static final int START_COMPONENT_OTHER = 5;
+
     /**
      * @see #getMonoticCreationTimeMs
      */
@@ -280,6 +300,11 @@
     private boolean mWasForceStopped;
 
     /**
+     * @see #getStartComponent()
+     */
+    private @StartComponent int mStartComponent;
+
+    /**
      * @hide *
      */
     @IntDef(
@@ -344,6 +369,21 @@
     public @interface LaunchMode {}
 
     /**
+     * @hide *
+     */
+    @IntDef(
+            prefix = {"START_COMPONENT_"},
+            value = {
+                START_COMPONENT_ACTIVITY,
+                START_COMPONENT_BROADCAST,
+                START_COMPONENT_CONTENT_PROVIDER,
+                START_COMPONENT_SERVICE,
+                START_COMPONENT_OTHER,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StartComponent {}
+
+    /**
      * @see #getStartupState
      * @hide
      */
@@ -480,6 +520,14 @@
     }
 
     /**
+     * @see #getStartComponent()
+     * @hide
+     */
+    public void setStartComponent(@StartComponent int startComponent) {
+        mStartComponent = startComponent;
+    }
+
+    /**
      * Current state of startup.
      *
      * Can be used to determine whether the object will have additional fields added as it may be
@@ -567,6 +615,11 @@
     /**
      * The reason code of what triggered the process's start.
      *
+     * Start reason provides granular reasoning on why the app is being started. Start reason should
+     * not be used for distinguishing between the component the app is being started for as some
+     * reasons may overlap with multiple components, see {@link #getStartComponent} for this
+     * functionality instead.
+     *
      * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p>
      */
     public @StartReason int getReason() {
@@ -654,6 +707,22 @@
         return mWasForceStopped;
     }
 
+    /**
+     * The component type that was being started which triggered the start.
+     *
+     * Start component should be used to accurately distinguish between the 4 component types:
+     * activity, service, broadcast, and content provider. This can be useful for optimizing
+     * startup flow by enabling the caller to only load the necessary dependencies for a specific
+     * component type. For more granular information on why the app is being started, see
+     * {@link #getReason}.
+     *
+     * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p>
+     */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO_COMPONENT)
+    public @StartComponent int getStartComponent() {
+        return mStartComponent;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -680,6 +749,8 @@
         dest.writeParcelable(mStartIntent, flags);
         dest.writeInt(mLaunchMode);
         dest.writeBoolean(mWasForceStopped);
+        dest.writeLong(mMonoticCreationTimeMs);
+        dest.writeInt(mStartComponent);
     }
 
     /** @hide */
@@ -703,6 +774,7 @@
         mLaunchMode = other.mLaunchMode;
         mWasForceStopped = other.mWasForceStopped;
         mMonoticCreationTimeMs = other.mMonoticCreationTimeMs;
+        mStartComponent = other.mStartComponent;
     }
 
     private ApplicationStartInfo(@NonNull Parcel in) {
@@ -726,6 +798,7 @@
         mLaunchMode = in.readInt();
         mWasForceStopped = in.readBoolean();
         mMonoticCreationTimeMs = in.readLong();
+        mStartComponent = in.readInt();
     }
 
     private static String intern(@Nullable String source) {
@@ -805,6 +878,7 @@
         proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode);
         proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped);
         proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonoticCreationTimeMs);
+        proto.write(ApplicationStartInfoProto.START_COMPONENT, mStartComponent);
         proto.end(token);
     }
 
@@ -892,6 +966,9 @@
                     mMonoticCreationTimeMs = proto.readLong(
                             ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS);
                     break;
+                case (int) ApplicationStartInfoProto.START_COMPONENT:
+                    mStartComponent = proto.readInt(ApplicationStartInfoProto.START_COMPONENT);
+                    break;
             }
         }
         proto.end(token);
@@ -918,8 +995,11 @@
                 .append(" reason=").append(reasonToString(mReason))
                 .append(" startType=").append(startTypeToString(mStartType))
                 .append(" launchMode=").append(mLaunchMode)
-                .append(" wasForceStopped=").append(mWasForceStopped)
-                .append('\n');
+                .append(" wasForceStopped=").append(mWasForceStopped);
+        if (Flags.appStartInfoComponent()) {
+            sb.append(" startComponent=").append(startComponentToString(mStartComponent));
+        }
+        sb.append('\n');
         if (mStartIntent != null) {
             sb.append(" intent=").append(mStartIntent.toString())
                 .append('\n');
@@ -963,6 +1043,18 @@
         };
     }
 
+    @FlaggedApi(Flags.FLAG_APP_START_INFO_COMPONENT)
+    private static String startComponentToString(@StartComponent int startComponent) {
+        return switch (startComponent) {
+            case START_COMPONENT_ACTIVITY -> "ACTIVITY";
+            case START_COMPONENT_BROADCAST -> "BROADCAST";
+            case START_COMPONENT_CONTENT_PROVIDER -> "CONTENT PROVIDER";
+            case START_COMPONENT_SERVICE -> "SERVICE";
+            case START_COMPONENT_OTHER -> "OTHER";
+            default -> "";
+        };
+    }
+
     /** @hide */
     @Override
     public boolean equals(@Nullable Object other) {
@@ -971,18 +1063,19 @@
         }
         final ApplicationStartInfo o = (ApplicationStartInfo) other;
         return mPid == o.mPid && mRealUid == o.mRealUid && mPackageUid == o.mPackageUid
-            && mDefiningUid == o.mDefiningUid && mReason == o.mReason
-            && mStartupState == o.mStartupState && mStartType == o.mStartType
-            && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName)
-            && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped
-            && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs;
+                && mDefiningUid == o.mDefiningUid && mReason == o.mReason
+                && mStartupState == o.mStartupState && mStartType == o.mStartType
+                && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName)
+                && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped
+                && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs
+                && mStartComponent == o.mStartComponent;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState,
                 mStartType, mLaunchMode, mProcessName, mStartupTimestampsNs,
-                mMonoticCreationTimeMs);
+                mMonoticCreationTimeMs, mStartComponent);
     }
 
     private boolean timestampsEquals(@NonNull ApplicationStartInfo other) {
diff --git a/core/java/android/app/BroadcastStickyCache.java b/core/java/android/app/BroadcastStickyCache.java
new file mode 100644
index 0000000..d6f061b
--- /dev/null
+++ b/core/java/android/app/BroadcastStickyCache.java
@@ -0,0 +1,229 @@
+/*
+ * 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.app;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbManager;
+import android.media.AudioManager;
+import android.net.ConnectivityManager;
+import android.net.TetheringManager;
+import android.net.nsd.NsdManager;
+import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.os.SystemProperties;
+import android.os.UpdateLock;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.view.WindowManagerPolicyConstants;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/** @hide */
+public class BroadcastStickyCache {
+
+    private static final String[] CACHED_BROADCAST_ACTIONS = {
+            AudioManager.ACTION_HDMI_AUDIO_PLUG,
+            AudioManager.ACTION_HEADSET_PLUG,
+            AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED,
+            AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED,
+            AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
+            AudioManager.RINGER_MODE_CHANGED_ACTION,
+            ConnectivityManager.CONNECTIVITY_ACTION,
+            Intent.ACTION_BATTERY_CHANGED,
+            Intent.ACTION_DEVICE_STORAGE_FULL,
+            Intent.ACTION_DEVICE_STORAGE_LOW,
+            Intent.ACTION_SIM_STATE_CHANGED,
+            NsdManager.ACTION_NSD_STATE_CHANGED,
+            TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED,
+            TetheringManager.ACTION_TETHER_STATE_CHANGED,
+            UpdateLock.UPDATE_LOCK_CHANGED,
+            UsbManager.ACTION_USB_STATE,
+            WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED,
+            WifiManager.NETWORK_STATE_CHANGED_ACTION,
+            WifiManager.SUPPLICANT_STATE_CHANGED_ACTION,
+            WifiManager.WIFI_STATE_CHANGED_ACTION,
+            WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
+            WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED,
+            "android.net.conn.INET_CONDITION_ACTION" // ConnectivityManager.INET_CONDITION_ACTION
+    };
+
+    @GuardedBy("sCachedStickyBroadcasts")
+    private static final ArrayList<CachedStickyBroadcast> sCachedStickyBroadcasts =
+            new ArrayList<>();
+
+    @GuardedBy("sCachedPropertyHandles")
+    private static final ArrayMap<String, SystemProperties.Handle> sCachedPropertyHandles =
+            new ArrayMap<>();
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static boolean useCache(@Nullable IntentFilter filter) {
+        if (!shouldCache(filter)) {
+            return false;
+        }
+        synchronized (sCachedStickyBroadcasts) {
+            final CachedStickyBroadcast cachedStickyBroadcast = getValueUncheckedLocked(filter);
+            if (cachedStickyBroadcast == null) {
+                return false;
+            }
+            final long version = cachedStickyBroadcast.propertyHandle.getLong(-1 /* def */);
+            return version > 0 && cachedStickyBroadcast.version == version;
+        }
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static void add(@Nullable IntentFilter filter, @Nullable Intent intent) {
+        if (!shouldCache(filter)) {
+            return;
+        }
+        synchronized (sCachedStickyBroadcasts) {
+            CachedStickyBroadcast cachedStickyBroadcast = getValueUncheckedLocked(filter);
+            if (cachedStickyBroadcast == null) {
+                final String key = getKey(filter.getAction(0));
+                final SystemProperties.Handle handle = SystemProperties.find(key);
+                final long version = handle == null ? -1 : handle.getLong(-1 /* def */);
+                if (version == -1) {
+                    return;
+                }
+                cachedStickyBroadcast = new CachedStickyBroadcast(filter, handle);
+                sCachedStickyBroadcasts.add(cachedStickyBroadcast);
+                cachedStickyBroadcast.intent = intent;
+                cachedStickyBroadcast.version = version;
+            } else {
+                cachedStickyBroadcast.intent = intent;
+                cachedStickyBroadcast.version = cachedStickyBroadcast.propertyHandle
+                        .getLong(-1 /* def */);
+            }
+        }
+    }
+
+    private static boolean shouldCache(@Nullable IntentFilter filter) {
+        if (!Flags.useStickyBcastCache()) {
+            return false;
+        }
+        if (filter == null || filter.safeCountActions() != 1) {
+            return false;
+        }
+        if (!ArrayUtils.contains(CACHED_BROADCAST_ACTIONS, filter.getAction(0))) {
+            return false;
+        }
+        return true;
+    }
+
+    @VisibleForTesting
+    @NonNull
+    public static String getKey(@NonNull String action) {
+        return "cache_key.system_server.sticky_bcast." + action;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @Nullable
+    public static Intent getIntentUnchecked(@NonNull IntentFilter filter) {
+        synchronized (sCachedStickyBroadcasts) {
+            final CachedStickyBroadcast cachedStickyBroadcast = getValueUncheckedLocked(filter);
+            return cachedStickyBroadcast.intent;
+        }
+    }
+
+    @GuardedBy("sCachedStickyBroadcasts")
+    @Nullable
+    private static CachedStickyBroadcast getValueUncheckedLocked(@NonNull IntentFilter filter) {
+        for (int i = sCachedStickyBroadcasts.size() - 1; i >= 0; --i) {
+            final CachedStickyBroadcast cachedStickyBroadcast = sCachedStickyBroadcasts.get(i);
+            if (IntentFilter.filterEquals(filter, cachedStickyBroadcast.filter)) {
+                return cachedStickyBroadcast;
+            }
+        }
+        return null;
+    }
+
+    public static void incrementVersion(@NonNull String action) {
+        if (!shouldIncrementVersion(action)) {
+            return;
+        }
+        final String key = getKey(action);
+        synchronized (sCachedPropertyHandles) {
+            SystemProperties.Handle handle = sCachedPropertyHandles.get(key);
+            final long version;
+            if (handle == null) {
+                handle = SystemProperties.find(key);
+                if (handle != null) {
+                    sCachedPropertyHandles.put(key, handle);
+                }
+            }
+            version = handle == null ? 0 : handle.getLong(0 /* def */);
+            SystemProperties.set(key, String.valueOf(version + 1));
+            if (handle == null) {
+                sCachedPropertyHandles.put(key, SystemProperties.find(key));
+            }
+        }
+    }
+
+    public static void incrementVersionIfExists(@NonNull String action) {
+        if (!shouldIncrementVersion(action)) {
+            return;
+        }
+        final String key = getKey(action);
+        synchronized (sCachedPropertyHandles) {
+            final SystemProperties.Handle handle = sCachedPropertyHandles.get(key);
+            if (handle == null) {
+                return;
+            }
+            final long version = handle.getLong(0 /* def */);
+            SystemProperties.set(key, String.valueOf(version + 1));
+        }
+    }
+
+    private static boolean shouldIncrementVersion(@NonNull String action) {
+        if (!Flags.useStickyBcastCache()) {
+            return false;
+        }
+        if (!ArrayUtils.contains(CACHED_BROADCAST_ACTIONS, action)) {
+            return false;
+        }
+        return true;
+    }
+
+    @VisibleForTesting
+    public static void clearForTest() {
+        synchronized (sCachedStickyBroadcasts) {
+            sCachedStickyBroadcasts.clear();
+        }
+        synchronized (sCachedPropertyHandles) {
+            sCachedPropertyHandles.clear();
+        }
+    }
+
+    private static final class CachedStickyBroadcast {
+        @NonNull public final IntentFilter filter;
+        @Nullable public Intent intent;
+        @IntRange(from = 0) public long version;
+        @NonNull public final SystemProperties.Handle propertyHandle;
+
+        CachedStickyBroadcast(@NonNull IntentFilter filter,
+                @NonNull SystemProperties.Handle propertyHandle) {
+            this.filter = filter;
+            this.propertyHandle = propertyHandle;
+        }
+    }
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 90fba29..ecef0db 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1921,10 +1921,19 @@
             }
         }
         try {
-            final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
-                    mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
-                    AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
-                    flags);
+            final Intent intent;
+            if (receiver == null && BroadcastStickyCache.useCache(filter)) {
+                intent = BroadcastStickyCache.getIntentUnchecked(filter);
+            } else {
+                intent = ActivityManager.getService().registerReceiverWithFeature(
+                        mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
+                        AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission,
+                        userId,
+                        flags);
+                if (receiver == null) {
+                    BroadcastStickyCache.add(filter, intent);
+                }
+            }
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
                 // TODO: determine at registration time if caller is
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index c6fa064..12deaec 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -29,16 +29,24 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
-import android.os.Handler;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 /**
  * The GameManager allows system apps to modify and query the game mode of apps.
+ *
+ * <p><b>Note:</b> After {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, some devices
+ * that do not support the GameManager features <i>may</i> not publish a GameManager instance.
+ * These device types include:
+ * <ul>
+ *      <li> Wear devices ({@link PackageManager#FEATURE_WATCH})
+ * </ul>
+ *
+ * <p>Therefore, you should always do a {@code null} check on the return value of
+ * {@link Context#getSystemService(Class)} and {@link Context#getSystemService(String)} when trying
+ * to obtain an instance of GameManager on the aforementioned device types.
  */
 @SystemService(Context.GAME_SERVICE)
 public final class GameManager {
@@ -46,7 +54,7 @@
     private static final String TAG = "GameManager";
 
     private final @Nullable Context mContext;
-    private final IGameManagerService mService;
+    private final @Nullable IGameManagerService mService;
 
     /** @hide */
     @IntDef(flag = false, prefix = {"GAME_MODE_"}, value = {
@@ -92,10 +100,9 @@
      */
     public static final int GAME_MODE_CUSTOM = 4;
 
-    GameManager(Context context, Handler handler) throws ServiceNotFoundException {
+    GameManager(Context context, @Nullable IGameManagerService service) {
         mContext = context;
-        mService = IGameManagerService.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.GAME_SERVICE));
+        mService = service;
     }
 
     /**
@@ -145,6 +152,7 @@
     // we don't want a binder call each time to check on behalf of an app using CompatChange.
     @SuppressWarnings("AndroidFrameworkCompatChange")
     private @GameMode int getGameModeImpl(@NonNull String packageName, int targetSdkVersion) {
+        if (mService == null) return GAME_MODE_UNSUPPORTED;
         final int gameMode;
         try {
             gameMode = mService.getGameMode(packageName,
@@ -176,6 +184,7 @@
     @UserHandleAware
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public @Nullable GameModeInfo getGameModeInfo(@NonNull String packageName) {
+        if (mService == null) return null;
         try {
             return mService.getGameModeInfo(packageName, mContext.getUserId());
         } catch (RemoteException e) {
@@ -196,6 +205,7 @@
     @UserHandleAware
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public void setGameMode(@NonNull String packageName, @GameMode int gameMode) {
+        if (mService == null) return;
         try {
             mService.setGameMode(packageName, gameMode, mContext.getUserId());
         } catch (RemoteException e) {
@@ -212,6 +222,7 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public @GameMode int[] getAvailableGameModes(@NonNull String packageName) {
+        if (mService == null) return new int[0];
         try {
             return mService.getAvailableGameModes(packageName, mContext.getUserId());
         } catch (RemoteException e) {
@@ -232,6 +243,7 @@
     @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public boolean isAngleEnabled(@NonNull String packageName) {
+        if (mService == null) return false;
         try {
             return mService.isAngleEnabled(packageName, mContext.getUserId());
         } catch (RemoteException e) {
@@ -246,6 +258,7 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public void notifyGraphicsEnvironmentSetup() {
+        if (mService == null) return;
         try {
             mService.notifyGraphicsEnvironmentSetup(
                     mContext.getPackageName(), mContext.getUserId());
@@ -259,6 +272,7 @@
      * @param gameState An object set to the current state.
      */
     public void setGameState(@NonNull GameState gameState) {
+        if (mService == null) return;
         try {
             mService.setGameState(mContext.getPackageName(), gameState, mContext.getUserId());
         } catch (RemoteException e) {
@@ -275,6 +289,7 @@
      */
     @TestApi
     public void setGameServiceProvider(@Nullable String packageName) {
+        if (mService == null) return;
         try {
             mService.setGameServiceProvider(packageName);
         } catch (RemoteException e) {
@@ -296,6 +311,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
     public void updateCustomGameModeConfiguration(@NonNull String packageName,
             @NonNull GameModeConfiguration gameModeConfig) {
+        if (mService == null) return;
         try {
             mService.updateCustomGameModeConfiguration(packageName, gameModeConfig,
                     mContext.getUserId());
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 9f3829e..06d01ec 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -92,6 +92,7 @@
             in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
             in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges,
             in long[] loggableCompatChanges, in SharedMemory serializedSystemFontMap,
+            in FileDescriptor applicationSharedMemoryFd,
             long startRequestedElapsedTime, long startRequestedUptime);
     void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
     void scheduleExit();
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e2bee64..8a54b5d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -258,4 +258,7 @@
     @EnforcePermission(allOf={"INTERACT_ACROSS_USERS", "ACCESS_NOTIFICATIONS"})
     void unregisterCallNotificationEventListener(String packageName, in UserHandle userHandle, in ICallNotificationEventCallback listener);
 
+    void setCanBePromoted(String pkg, int uid, boolean promote, boolean fromUser);
+    boolean appCanBePromoted(String pkg, int uid);
+    boolean canBePromoted(String pkg);
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 81d2c89..a39f216 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -121,7 +121,6 @@
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -772,10 +771,43 @@
     @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
     public static final int FLAG_SILENT = 1 << 17;  //0x00020000
 
-    private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
-            BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
-            DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
-            MessagingStyle.class, CallStyle.class);
+    /**
+     * Bit to be bitwise-ored into the {@link #flags} field that should be
+     * set by the system if this notification is a promoted ongoing notification, either via a
+     * user setting or allowlist.
+     *
+     * Applications cannot set this flag directly, but the posting app and
+     * {@link android.service.notification.NotificationListenerService} can read it.
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final int FLAG_PROMOTED_ONGOING = 0x00040000;
+
+    private static final Set<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Set.of(
+            BigTextStyle.class,
+            BigPictureStyle.class,
+            InboxStyle.class,
+            MediaStyle.class,
+            DecoratedCustomViewStyle.class,
+            DecoratedMediaCustomViewStyle.class,
+            MessagingStyle.class,
+            CallStyle.class
+    );
+
+    private static boolean isPlatformStyle(Style style) {
+        if (style == null) {
+            return false;
+        }
+
+        if (PLATFORM_STYLE_CLASSES.contains(style.getClass())) {
+            return true;
+        }
+
+        if (Flags.apiRichOngoing()) {
+            return style.getClass() == ProgressStyle.class;
+        }
+
+        return false;
+    }
 
     /** @hide */
     @IntDef(flag = true, prefix = {"FLAG_"}, value = {
@@ -1609,6 +1641,66 @@
     public static final String EXTRA_COLORIZED = "android.colorized";
 
     /**
+     * {@link #extras} key: an arraylist of {@link android.app.Notification.ProgressStyle.Segment}
+     * bundles provided by a
+     * {@link android.app.Notification.ProgressStyle} notification as supplied to
+     * {@link ProgressStyle#setProgressSegments}
+     * or {@link ProgressStyle#addProgressSegment(ProgressStyle.Segment)}.
+     * This extra is a parcelable array list of bundles.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_PROGRESS_SEGMENTS = "android.progressSegments";
+
+    /**
+     * {@link #extras} key: an arraylist of {@link android.app.Notification.ProgressStyle.Step}
+     * bundles provided by a
+     * {@link android.app.Notification.ProgressStyle} notification as supplied to
+     * {@link ProgressStyle#setProgressSteps}
+     * or {@link ProgressStyle#addProgressStep(ProgressStyle.Step)}.
+     * This extra is a parcelable array list of bundles.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_PROGRESS_STEPS = "android.progressSteps";
+
+    /**
+     * {@link #extras} key: whether the progress bar should be styled by its progress as
+     * supplied to {@link ProgressStyle#setStyledByProgress}.
+     * This extra is a boolean.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_STYLED_BY_PROGRESS = "android.styledByProgress";
+
+    /**
+     * {@link #extras} key: this is an {@link Icon} of an image to be
+     * shown as progress bar progress tracker icon in {@link ProgressStyle}, supplied to
+     *{@link ProgressStyle#setProgressTrackerIcon(Icon)}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_PROGRESS_TRACKER_ICON = "android.progressTrackerIcon";
+
+    /**
+     * {@link #extras} key: this is an {@link Icon} of an image to be
+     * shown at the beginning of the progress bar in {@link ProgressStyle}, supplied to
+     *{@link ProgressStyle#setProgressStartIcon(Icon)}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_PROGRESS_START_ICON = "android.progressStartIcon";
+
+    /**
+     * {@link #extras} key: this is an {@link Icon} of an image to be
+     * shown at the end of the progress bar in {@link ProgressStyle}, supplied to
+     *{@link ProgressStyle#setProgressEndIcon(Icon)}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_PROGRESS_END_ICON = "android.progressEndIcon";
+
+    /**
      * @hide
      */
     public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
@@ -3061,6 +3153,9 @@
 
         if (Flags.apiRichOngoing()) {
             visitIconUri(visitor, extras.getParcelable(EXTRA_ENROUTE_OVERLAY_ICON, Icon.class));
+            visitIconUri(visitor, extras.getParcelable(EXTRA_PROGRESS_TRACKER_ICON, Icon.class));
+            visitIconUri(visitor, extras.getParcelable(EXTRA_PROGRESS_START_ICON, Icon.class));
+            visitIconUri(visitor, extras.getParcelable(EXTRA_PROGRESS_END_ICON, Icon.class));
         }
 
         if (mBubbleMetadata != null) {
@@ -3110,6 +3205,53 @@
     }
 
     /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+    public boolean containsCustomViews() {
+        return contentView != null
+                || bigContentView != null
+                || headsUpContentView != null
+                || (publicVersion != null
+                && (publicVersion.contentView != null
+                || publicVersion.bigContentView != null
+                || publicVersion.headsUpContentView != null));
+    }
+
+    /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+    public boolean hasTitle() {
+        return extras != null
+                && (!TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE))
+                || !TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE_BIG)));
+    }
+
+    /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+    public boolean hasPromotableStyle() {
+        //TODO(b/367739672): Add progress style
+        return extras == null || !extras.containsKey(Notification.EXTRA_TEMPLATE)
+                || isStyle(Notification.BigPictureStyle.class)
+                || isStyle(Notification.BigTextStyle.class)
+                || isStyle(Notification.CallStyle.class);
+    }
+
+    /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
+    public boolean hasPromotableCharacteristics() {
+        return isColorized()
+                && hasTitle()
+                && !containsCustomViews()
+                && hasPromotableStyle();
+    }
+
+    /**
      * Whether this notification was posted by a headless system app.
      *
      * If we don't have enough information to figure this out, this will return false. Therefore,
@@ -6572,7 +6714,7 @@
             // Custom views which come from a platform style class are safe, and thus do not need to
             // be wrapped.  Any subclass of those styles has the opportunity to make arbitrary
             // changes to the RemoteViews, and thus can't be trusted as a fully vetted view.
-            if (fromStyle && PLATFORM_STYLE_CLASSES.contains(mStyle.getClass())) {
+            if (fromStyle && isPlatformStyle(mStyle)) {
                 return false;
             }
             return mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S;
@@ -7636,7 +7778,6 @@
 
         if (mLargeIcon != null || largeIcon != null) {
             Resources resources = context.getResources();
-            Class<? extends Style> style = getNotificationStyle();
             int maxSize = resources.getDimensionPixelSize(isLowRam
                     ? R.dimen.notification_right_icon_size_low_ram
                     : R.dimen.notification_right_icon_size);
@@ -7914,6 +8055,12 @@
                 return innerClass;
             }
         }
+
+        if (Flags.apiRichOngoing()) {
+            if (templateClass.equals(ProgressStyle.class.getName())) {
+                return ProgressStyle.class;
+            }
+        }
         return null;
     }
 
@@ -8782,6 +8929,16 @@
         }
 
         /**
+         * @hide
+         */
+        public boolean displayCustomViewInline() {
+            // This is a lie; True is returned for conversations to make sure that the custom
+            // view is not used instead of the template, but it will not actually be included.
+            return Flags.notificationNoCustomViewConversations()
+                    && mConversationType != CONVERSATION_TYPE_LEGACY;
+        }
+
+        /**
          * @return the text that should be displayed in the statusBar when heads upped.
          * If {@code null} is returned, the default implementation will be used.
          *
@@ -11153,6 +11310,724 @@
         }
     }
 
+
+    /**
+     * A Notification Style used to to define a notification whose expanded state includes
+     * a highly customizable progress bar with segments, steps, a custom tracker icon,
+     * and custom icons at the start and end of the progress bar.
+     *
+     * This style is suggested for use cases where the app is showing a tracker to the
+     * user of a thing they are interested in: the location of a car on its way
+     * to pick them up, food being delivered, or their own progress in a navigation
+     * journey.
+     *
+     * To use this style with your Notification, feed it to
+     * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
+     * <pre class="prettyprint">
+     * new Notification.Builder(context)
+     *   .setSmallIcon(R.drawable.ic_notification)
+     *   .setColor(Color.GREEN)
+     *   .setColorized(true)
+     *   .setContentTitle("Arrive 10:08 AM").
+     *   .setContentText("Dominique Ansel Bakery Soho")
+     *   .addAction(new Notification.Action("Exit navigation",...))
+     *   .setStyle(new Notification.ProgressStyle()
+     *       .setStyledByProgress(false)
+     *       .setProgress(456)
+     *       .setProgressTrackerIcon(Icon.createWithResource(R.drawable.ic_driving_tracker))
+     *       .addProgressSegment(new Segment(41).setColor(Color.BLACK))
+     *       .addProgressSegment(new Segment(552).setColor(Color.YELLOW))
+     *       .addProgressSegment(new Segment(253).setColor(Color.YELLOW))
+     *       .addProgressSegment(new Segment(94).setColor(Color.BLUE))
+     *       .addProgressStep(new Step(60).setColor(Color.RED))
+     *       .addProgressStep(new Step(560).setColor(Color.YELLOW))
+     *   )
+     * </pre>
+     *
+     *
+     *
+     * NOTE: The progress bar layout will be mirrored for RTL layout.
+     * NOTE: The extras set by {@link Notification.Builder#setProgress} will be overridden by
+     * the values set on this style object when the notification is built.
+     *
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static class ProgressStyle extends Notification.Style {
+        private static final String KEY_ELEMENT_STABLE_ID = "stableId";
+        private static final String KEY_ELEMENT_COLOR = "colorInt";
+        private static final String KEY_SEGMENT_LENGTH = "length";
+        private static final String KEY_STEP_POSITION = "position";
+
+        private static final int MAX_PROGRESS_SEGMENT_LIMIT = 15;
+        private static final int MAX_PROGRESS_STEP_LIMIT = 5;
+        private static final int DEFAULT_PROGRESS_MAX = 100;
+
+        private List<Segment> mProgressSegments = new ArrayList<>();
+        private List<Step> mProgressSteps = new ArrayList<>();
+
+        private int mProgress = 0;
+
+        private boolean mIndeterminate;
+
+        private boolean mIsStyledByProgress = true;
+
+        @Nullable
+        private Icon mTrackerIcon;
+        @Nullable
+        private Icon mStartIcon;
+        @Nullable
+        private Icon mEndIcon;
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+
+            final ProgressStyle progressStyle = (ProgressStyle) other;
+
+            /**
+             * @see #setProgressIndeterminate
+             */
+            if (!Objects.equals(mIndeterminate, progressStyle.mIndeterminate)) {
+                return true;
+            }
+            boolean nonIndeterminateCheckResult = false;
+            if (!mIndeterminate) {
+                nonIndeterminateCheckResult = !Objects.equals(mProgress, progressStyle.mProgress)
+                        || !Objects.equals(mIsStyledByProgress, progressStyle.mIsStyledByProgress)
+                        || !Objects.equals(mProgressSegments, progressStyle.mProgressSegments)
+                        || !Objects.equals(mProgressSteps, progressStyle.mProgressSteps)
+                        || !Objects.equals(mTrackerIcon, progressStyle.mTrackerIcon);
+            }
+
+            return !Objects.equals(mStartIcon, progressStyle.mStartIcon)
+                    || !Objects.equals(mEndIcon, progressStyle.mEndIcon)
+                    || nonIndeterminateCheckResult;
+        }
+
+        /**
+         * Gets the segments that define the background layer of the progress bar.
+         *
+         * If no segments are provided, the progress bar will be rendered with a single segment
+         * with length 100 and default color.
+         *
+         * @see #setProgressSegments
+         * @see #addProgressSegment
+         * @see Segment
+         */
+        public @NonNull List<Segment> getProgressSegments() {
+            return mProgressSegments;
+        }
+
+        /**
+         * Sets or replaces the segments of the progress bar.
+         *
+         * Segments allow for creating progress bars with multiple colors or sections
+         * to represent different stages or categories of progress.
+         * For example, Traffic conditions along a navigation journey.
+         * @see Segment
+         */
+        public @NonNull ProgressStyle setProgressSegments(@NonNull List<Segment> progressSegments) {
+            mProgressSegments = new ArrayList<>(progressSegments.size());
+            return this;
+        }
+
+        /**
+         * Appends a segment to the end of the progress bar.
+         *
+         * Segments allow for creating progress bars with multiple colors or sections
+         * to represent different stages or categories of progress.
+         * For example, Traffic conditions along a navigation journey.
+         * @see Segment
+         */
+        public @NonNull ProgressStyle addProgressSegment(@NonNull Segment segment) {
+            if (mProgressSegments == null) {
+                mProgressSegments = new ArrayList<>();
+            }
+            mProgressSegments.add(segment);
+
+            return this;
+        }
+
+        /**
+         * Gets the steps that are displayed on the progress bar.
+         *.
+         * @see #setProgressSteps
+         * @see #addProgressStep
+         * @see Step
+         */
+        public @NonNull List<Step> getProgressSteps() {
+            return mProgressSteps;
+        }
+
+        /**
+         * Replaces all the progress steps.
+         *
+         * Steps are designated points within a progressbar to visualize
+         * distinct stages or milestones.
+         * For example, you might use steps to mark stops in a multi-stop
+         * navigation journey, where each step represents a destination.
+         * @see Step
+         */
+        public @NonNull ProgressStyle setProgressSteps(@NonNull List<Step> steps) {
+            mProgressSteps = new ArrayList<>(steps);
+            return this;
+        }
+
+        /**
+         * Adds another step.
+         *
+         * Steps are designated points within a progressbar to visualize
+         * distinct stages or milestones.
+         * For example, you might use steps to mark stops in a multi-stop
+         * navigation journey, where each step represents a destination.
+         *
+         * Steps can be added in any order, as their
+         * position within the progress bar is determined by their individual
+         * {@link Step#getPosition()}.
+         * @see Step
+         */
+        public @NonNull ProgressStyle addProgressStep(@NonNull Step step) {
+            if (mProgressSteps == null) {
+                mProgressSteps = new ArrayList<>();
+            }
+            mProgressSteps.add(step);
+
+            return this;
+        }
+
+        /**
+         * Gets the progress value of the progress bar.
+         * @see #setProgress
+         */
+        public int getProgress() {
+            return mProgress;
+        }
+
+        /**
+        * Specifies the progress (in the same units as {@link Segment#getLength()})
+        * of the tracker along the length of the bar.
+        *
+        * The max progress value is the sum of all Segment lengths.
+        * The default value is 0.
+        */
+        public @NonNull ProgressStyle setProgress(int progress) {
+            mProgress = progress;
+            return this;
+        }
+
+        /**
+         * Gets the sum of the lengths of all Segments in the style, which
+         * defines the maximum progress. Defaults to 100 when segments are omitted.
+         */
+        public int getProgressMax() {
+            final List<Segment> progressSegment = mProgressSegments;
+            if (progressSegment == null || progressSegment.isEmpty()) {
+                return DEFAULT_PROGRESS_MAX;
+            } else {
+                int progressMax = 0;
+                int validSegmentCount = 0;
+                for (int i = 0; i < progressSegment.size()
+                        && validSegmentCount < MAX_PROGRESS_SEGMENT_LIMIT; i++) {
+                    int segmentLength = progressSegment.get(i).getLength();
+                    if (segmentLength > 0) {
+                        try {
+                            progressMax = Math.addExact(progressMax, segmentLength);
+                            validSegmentCount++;
+                        } catch (ArithmeticException e) {
+                            Log.e(TAG,
+                                    "Notification.ProgressStyle segment total overflowed.", e);
+                            return DEFAULT_PROGRESS_MAX;
+                        }
+                    }
+                }
+
+                if (validSegmentCount == 0) {
+                    return DEFAULT_PROGRESS_MAX;
+                }
+
+                return progressMax;
+            }
+
+        }
+
+        /**
+         * Get indeterminate value of the progress bar.
+         * @see #setProgressIndeterminate
+         */
+        public boolean isProgressIndeterminate() {
+            return mIndeterminate;
+        }
+
+        /**
+         * Used to indicate an initialization state without a known progress amount.
+         * When specified, the following fields are ignored:
+         * @see #setProgress
+         * @see #setProgressSegments
+         * @see #setProgressSteps
+         * @see #setProgressTrackerIcon
+         * @see #setStyledByProgress
+         *
+         * If the app provides exactly one Segment, that segment's color will be
+         * used to style the indeterminate bar.
+         */
+        public @NonNull ProgressStyle setProgressIndeterminate(boolean indeterminate) {
+            mIndeterminate = indeterminate;
+            return this;
+        }
+
+        /**
+         * Gets whether the progress bar's style is based on its progress.
+         * @see #setStyledByProgress
+         */
+        public boolean isStyledByProgress() {
+            return mIsStyledByProgress;
+        }
+
+        /**
+         * Indicates whether the segments and steps will be styled differently
+         * based on whether they are behind or ahead of the current progress.
+         * When true, segments appearing ahead of the current progress will be given a
+         * slightly different appearance to indicate that it is part of the progress bar
+         * that is not "filled".
+         * When false, all segments will be given the filled appearance, and it will be
+         * the app's responsibility to use #setProgressTrackerIcon or segment colors
+         * to make the current progress clear to the user.
+         * the default value is true.
+         */
+        public @NonNull ProgressStyle setStyledByProgress(boolean enabled) {
+            mIsStyledByProgress = enabled;
+            return this;
+        }
+
+
+        /**
+         * Gets the progress tracker icon for the progress bar.
+         * @see #setProgressTrackerIcon
+         */
+        public @Nullable Icon getProgressTrackerIcon() {
+            return mTrackerIcon;
+        }
+
+        /**
+         * An optional icon that can appear as an overlay on the bar at the point of
+         * current progress.
+         * Aspect ratio may be anywhere from 2:1 to 1:2; content outside that
+         * aspect ratio range will be cropped.
+         * This icon will be mirrored in RTL.
+         */
+        public @NonNull ProgressStyle setProgressTrackerIcon(@Nullable Icon trackerIcon) {
+            mTrackerIcon = trackerIcon;
+            return this;
+        }
+
+        /**
+         * Gets the progress bar start icon.
+         * @see #setProgressStartIcon
+         */
+        public @Nullable Icon getProgressStartIcon() {
+            return mStartIcon;
+        }
+
+        /**
+         * An optional square icon that appears at the start of the progress bar.
+         * This icon will be cropped to its central square.
+         * This icon will NOT be mirrored in RTL layouts.
+         */
+        public @NonNull ProgressStyle setProgressStartIcon(@Nullable Icon startIcon) {
+            mStartIcon = startIcon;
+            return this;
+        }
+
+        /**
+         * Gets the progress bar end icon.
+         * @see #setProgressEndIcon(Icon)
+         */
+        public @Nullable Icon getProgressEndIcon() {
+            return mEndIcon;
+        }
+
+        /**
+         * An optional square icon that appears at the end of the progress bar.
+         * This icon will be cropped to its central square.
+         * This icon will NOT be mirrored in RTL layouts.
+         */
+        public @NonNull ProgressStyle setProgressEndIcon(@Nullable Icon endIcon) {
+            mEndIcon = endIcon;
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void purgeResources() {
+            super.purgeResources();
+            if (mTrackerIcon != null) {
+                mTrackerIcon.convertToAshmem();
+            }
+            if (mStartIcon != null) {
+                mStartIcon.convertToAshmem();
+            }
+            if (mEndIcon != null) {
+                mEndIcon.convertToAshmem();
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void reduceImageSizes(Context context) {
+            super.reduceImageSizes(context);
+
+            final Resources resources = context.getResources();
+
+            int progressIconSize =
+                    resources.getDimensionPixelSize(R.dimen.notification_progress_icon_size);
+            if (mStartIcon != null) {
+                mStartIcon.scaleDownIfNecessary(progressIconSize, progressIconSize);
+            }
+            if (mEndIcon != null) {
+                mEndIcon.scaleDownIfNecessary(progressIconSize, progressIconSize);
+            }
+            if (mTrackerIcon != null) {
+                int progressTrackerWidth = resources.getDimensionPixelSize(
+                        R.dimen.notification_progress_tracker_width);
+                int progressTrackerHeight = resources.getDimensionPixelSize(
+                        R.dimen.notification_progress_tracker_height);
+                mTrackerIcon.scaleDownIfNecessary(progressTrackerWidth, progressTrackerHeight);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void addExtras(Bundle extras) {
+            super.addExtras(extras);
+            extras.putParcelableArrayList(EXTRA_PROGRESS_SEGMENTS,
+                    getProgressSegmentsAsBundleList(mProgressSegments));
+            extras.putParcelableArrayList(EXTRA_PROGRESS_STEPS,
+                    getProgressStepsAsBundleList(mProgressSteps));
+
+            extras.putInt(EXTRA_PROGRESS, mProgress);
+            extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mIndeterminate);
+            extras.putInt(EXTRA_PROGRESS_MAX, getProgressMax());
+            extras.putBoolean(EXTRA_STYLED_BY_PROGRESS, mIsStyledByProgress);
+
+            if (mTrackerIcon != null) {
+                extras.putParcelable(EXTRA_PROGRESS_TRACKER_ICON, mTrackerIcon);
+            } else {
+                extras.remove(EXTRA_PROGRESS_TRACKER_ICON);
+            }
+
+            if (mStartIcon != null) {
+                extras.putParcelable(EXTRA_PROGRESS_START_ICON, mStartIcon);
+            } else {
+                extras.remove(EXTRA_PROGRESS_START_ICON);
+            }
+
+            if (mEndIcon != null) {
+                extras.putParcelable(EXTRA_PROGRESS_END_ICON, mEndIcon);
+            } else {
+                extras.remove(EXTRA_PROGRESS_END_ICON);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        protected void restoreFromExtras(Bundle extras) {
+            super.restoreFromExtras(extras);
+            mProgressSegments = getProgressSegmentsFromBundleList(
+                    extras.getParcelableArrayList(EXTRA_PROGRESS_SEGMENTS, Bundle.class));
+            mProgress = extras.getInt(EXTRA_PROGRESS, 0);
+            mIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE, false);
+            mIsStyledByProgress = extras.getBoolean(EXTRA_STYLED_BY_PROGRESS, true);
+            mTrackerIcon = extras.getParcelable(EXTRA_PROGRESS_TRACKER_ICON, Icon.class);
+            mStartIcon = extras.getParcelable(EXTRA_PROGRESS_START_ICON, Icon.class);
+            mEndIcon = extras.getParcelable(EXTRA_PROGRESS_END_ICON, Icon.class);
+            mProgressSteps = getProgressStepsFromBundleList(
+                    extras.getParcelableArrayList(EXTRA_PROGRESS_STEPS, Bundle.class));
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean displayCustomViewInline() {
+            // This is a lie; True is returned for progress notifications to make sure
+            // that the custom view is not used instead of the template, but it will not
+            // actually be included.
+            return true;
+        }
+
+        private static @NonNull ArrayList<Bundle> getProgressSegmentsAsBundleList(
+                @Nullable List<Segment> progressSegments) {
+            final ArrayList<Bundle> segments = new ArrayList<>();
+            if (progressSegments != null && !progressSegments.isEmpty()) {
+                for (int i = 0; i < progressSegments.size(); i++) {
+                    final Segment segment = progressSegments.get(i);
+                    if (segment.getLength() <= 0) {
+                        continue;
+                    }
+
+                    final Bundle bundle = new Bundle();
+                    bundle.putInt(KEY_SEGMENT_LENGTH, segment.getLength());
+                    bundle.putInt(KEY_ELEMENT_STABLE_ID, segment.getStableId());
+                    bundle.putInt(KEY_ELEMENT_COLOR, segment.getColor());
+
+                    segments.add(bundle);
+                }
+            }
+
+            return segments;
+        }
+
+        private static @NonNull List<Segment> getProgressSegmentsFromBundleList(
+                @Nullable List<Bundle> segmentBundleList) {
+            final ArrayList<Segment> segments = new ArrayList<>();
+            if (segmentBundleList != null && !segmentBundleList.isEmpty()) {
+                for (int i = 0; i < segmentBundleList.size(); i++) {
+                    final Bundle segmentBundle = segmentBundleList.get(i);
+                    final int length = segmentBundle.getInt(KEY_SEGMENT_LENGTH);
+                    if (length <= 0) {
+                        continue;
+                    }
+
+                    final int stableId = segmentBundle.getInt(KEY_ELEMENT_STABLE_ID);
+                    final int color = segmentBundle.getInt(KEY_ELEMENT_COLOR,
+                            Notification.COLOR_DEFAULT);
+                    final Segment segment = new Segment(length)
+                            .setStableId(stableId).setColor(color);
+
+                    segments.add(segment);
+                }
+            }
+
+            return segments;
+        }
+
+        private static @NonNull ArrayList<Bundle> getProgressStepsAsBundleList(
+                @Nullable List<Step> progressSteps) {
+            final ArrayList<Bundle> steps = new ArrayList<>();
+            if (progressSteps != null && !progressSteps.isEmpty()) {
+                for (int i = 0; i < progressSteps.size(); i++) {
+                    final Step step = progressSteps.get(i);
+                    if (step.getPosition() < 0) {
+                        continue;
+                    }
+
+                    final Bundle bundle = new Bundle();
+                    bundle.putInt(KEY_STEP_POSITION, step.getPosition());
+                    bundle.putInt(KEY_ELEMENT_STABLE_ID, step.getStableId());
+                    bundle.putInt(KEY_ELEMENT_COLOR, step.getColor());
+
+                    steps.add(bundle);
+                }
+            }
+
+            return steps;
+        }
+
+        private static @NonNull List<Step> getProgressStepsFromBundleList(
+                @Nullable List<Bundle> stepBundleList) {
+            final ArrayList<Step> steps = new ArrayList<>();
+
+            if (stepBundleList != null && !stepBundleList.isEmpty()) {
+                for (int i = 0; i < stepBundleList.size(); i++) {
+                    final Bundle segmentBundle = stepBundleList.get(i);
+                    final int position = segmentBundle.getInt(KEY_STEP_POSITION);
+                    if (position < 0) {
+                        continue;
+                    }
+                    final int stableId = segmentBundle.getInt(KEY_ELEMENT_STABLE_ID);
+                    final int color = segmentBundle.getInt(KEY_ELEMENT_COLOR,
+                            Notification.COLOR_DEFAULT);
+                    final Step step = new Step(position).setStableId(stableId).setColor(color);
+                    steps.add(step);
+                }
+            }
+
+            return steps;
+        }
+
+        /**
+         * A segment of the progress bar, which defines its length and color.
+         * Segments allow for creating progress bars with multiple colors or sections
+         * to represent different stages or categories of progress.
+         * For example, Traffic conditions along a navigation journey.
+         */
+        public static final class Segment {
+            private int mLength;
+            private int mStableId = 0;
+            @ColorInt
+            private int mColor = Notification.COLOR_DEFAULT;
+
+            /**
+             * Create a segment with a non-zero length.
+             * @param length
+             * See {@link #getLength}
+             */
+            public Segment(int length) {
+                mLength = length;
+            }
+
+            /**
+             * The length of this Segment within the progress bar.
+             * This value has no units, it is just relative to the length of other segments,
+             * and the value provided to {@link ProgressStyle#setProgress}.
+             */
+            public int getLength() {
+                return mLength;
+            }
+
+            /**
+             * Gets the stable id of this Segment.
+             *
+             * @see #setStableId
+             */
+            public int getStableId() {
+                return mStableId;
+            }
+
+            /**
+             * Optional ID used to uniquely identify the element across updates.
+             */
+            public @NonNull Segment setStableId(int stableId) {
+                mStableId = stableId;
+                return this;
+            }
+
+            /**
+             * Returns the color of this Segment.
+             *
+             * @see #setColor
+             */
+            @ColorInt
+            public int getColor() {
+                return mColor;
+            }
+
+            /**
+             * Optional color of this Segment
+             */
+            public @NonNull Segment setColor(@ColorInt int color) {
+                mColor = color;
+                return this;
+            }
+
+            /**
+             * Needed for {@link Notification.Style#areNotificationsVisiblyDifferent}
+             */
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+                Segment segment = (Segment) o;
+                return mLength == segment.mLength && mStableId == segment.mStableId
+                        && mColor == segment.mColor;
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(mLength, mStableId, mColor);
+            }
+        }
+
+        /**
+         * A step within the progress bar, defining its position and color.
+         * Steps are designated points within a progressbar to visualize
+         * distinct stages or milestones.
+         * For example, you might use steps to mark stops in a multi-stop
+         * navigation journey, where each step represents a destination.
+         */
+        public static final class Step {
+
+            private int mPosition;
+            private int mStableId;
+            @ColorInt
+            private int mColor = Notification.COLOR_DEFAULT;
+
+            /**
+             * Create a step element.
+             * The position of this step on the progress bar
+             * relative to {@link ProgressStyle#getProgressMax}
+             * @param position
+             * See {@link #getPosition}
+             */
+            public Step(int position) {
+                mPosition = position;
+            }
+
+            /**
+             * Gets the position of this Step.
+             * The position of this step on the progress bar
+             * relative to {@link ProgressStyle#getProgressMax}.
+             */
+            public int getPosition() {
+                return mPosition;
+            }
+
+
+            /**
+             * Optional ID used to uniqurely identify the element across updates.
+             */
+            public int getStableId() {
+                return mStableId;
+            }
+
+            /**
+             * Optional ID used to uniqurely identify the element across updates.
+             */
+            public @NonNull Step setStableId(int stableId) {
+                mStableId = stableId;
+                return this;
+            }
+
+            /**
+             * Returns the color of this Segment.
+             *
+             * @see #setColor
+             */
+            @ColorInt
+            public int getColor() {
+                return mColor;
+            }
+
+            /**
+             * Optional color of this Segment
+             */
+            public @NonNull Step setColor(@ColorInt int color) {
+                mColor = color;
+                return this;
+            }
+
+            /**
+             * Needed for {@link Notification.Style#areNotificationsVisiblyDifferent}
+             */
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+                Step step = (Step) o;
+                return mPosition == step.mPosition && mStableId == step.mStableId
+                        && mColor == step.mColor;
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(mPosition, mStableId, mColor);
+            }
+        }
+    }
+
     /**
      * Notification style for custom views that are decorated by the system
      *
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 83f9ff7..c7b84ae 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -953,6 +953,36 @@
     }
 
     /**
+     * Returns whether the calling app's properly formatted notifications can appear in a promoted
+     * format, which may result in higher ranking, appearances on additional surfaces, and richer
+     * presentation.
+     */
+    @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public boolean canPostPromotedNotifications() {
+        INotificationManager service = getService();
+        try {
+            return service.canBePromoted(mContext.getPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Setter for {@link #canPostPromotedNotifications()}. Only callable by the OS.
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void setCanPostPromotedNotifications(@NonNull String pkg, int uid, boolean allowed) {
+        INotificationManager service = getService();
+        try {
+            service.setCanBePromoted(pkg, uid, allowed, true);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a group container for {@link NotificationChannel} objects.
      *
      * This can be used to rename an existing group.
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index cd7e40c..d363e19 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -38,6 +38,7 @@
 per-file IGameManager* = file:/GAME_MANAGER_OWNERS
 per-file IGameMode* = file:/GAME_MANAGER_OWNERS
 per-file BackgroundStartPrivileges.java = file:/BAL_OWNERS
+per-file activity_manager.aconfig = file:/ACTIVITY_MANAGER_OWNERS
 
 # ActivityThread
 per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 0e761fc..c17da24 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -54,198 +54,8 @@
  * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
  * but doesn't hold a lock across data fetches on query misses.
  *
- * The intended use case is caching frequently-read, seldom-changed information normally
- * retrieved across interprocess communication. Imagine that you've written a user birthday
- * information daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface
- * over binder. That binder interface looks something like this:
- *
- * <pre>
- * parcelable Birthday {
- *   int month;
- *   int day;
- * }
- * interface IUserBirthdayService {
- *   Birthday getUserBirthday(int userId);
- * }
- * </pre>
- *
- * Suppose the service implementation itself looks like this...
- *
- * <pre>
- * public class UserBirthdayServiceImpl implements IUserBirthdayService {
- *   private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
- *   {@literal @}Override
- *   public synchronized Birthday getUserBirthday(int userId) {
- *     return mUidToBirthday.get(userId);
- *   }
- *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
- *     mUidToBirthday.clear();
- *     mUidToBirthday.putAll(uidToBirthday);
- *   }
- * }
- * </pre>
- *
- * ... and we have a client in frameworks (loaded into every app process) that looks
- * like this:
- *
- * <pre>
- * public class ActivityThread {
- *   ...
- *   public Birthday getUserBirthday(int userId) {
- *     return GetService("birthdayd").getUserBirthday(userId);
- *   }
- *   ...
- * }
- * </pre>
- *
- * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call
- * to the birthdayd process and consult its database of birthdays. If we query user birthdays
- * frequently, we do a lot of work that we don't have to do, since user birthdays
- * change infrequently.
- *
- * PropertyInvalidatedCache is part of a pattern for optimizing this kind of
- * information-querying code. Using {@code PropertyInvalidatedCache}, you'd write the client
- * this way:
- *
- * <pre>
- * public class ActivityThread {
- *   ...
- *   private final PropertyInvalidatedCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
- *       new PropertyInvalidatedCache.QueryHandler&lt;Integer, Birthday&gt;() {
- *           {@literal @}Override
- *           public Birthday apply(Integer) {
- *              return GetService("birthdayd").getUserBirthday(userId);
- *           }
- *       };
- *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
- *   private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
- *   private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
- *     PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(
- *             BDAY_CACHE_MAX, MODULE_SYSTEM, "getUserBirthday", mBirthdayQuery);
- *
- *   public void disableUserBirthdayCache() {
- *     mBirthdayCache.disableForCurrentProcess();
- *   }
- *   public void invalidateUserBirthdayCache() {
- *     mBirthdayCache.invalidateCache();
- *   }
- *   public Birthday getUserBirthday(int userId) {
- *     return mBirthdayCache.query(userId);
- *   }
- *   ...
- * }
- * </pre>
- *
- * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
- * for the first time; on subsequent queries, we return the already-known Birthday object.
- *
- * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
- * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
- * string is permitted.  The third parameters is the name of the API being cached; this, too, can
- * any value.  The fourth is the name of the cache.  The cache is usually named after th API.
- * Some things you must know about the three strings:
- * <list>
- * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
- * Usually, the SELinux rules permit a process to write a system property (and therefore
- * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}.  This means that
- * although the cache can be constructed with any module string, whatever string is chosen must be
- * consistent with the SELinux configuration.
- * <ul> The API name can be any string of alphanumeric characters.  All caches with the same API
- * are invalidated at the same time.  If a server supports several caches and all are invalidated
- * in common, then it is most efficient to assign the same API string to every cache.
- * <ul> The cache name can be any string.  In debug output, the name is used to distiguish between
- * caches with the same API name.  The cache name is also used when disabling caches in the
- * current process.  So, invalidation is based on the module+api but disabling (which is generally
- * a once-per-process operation) is based on the cache name.
- * </list>
- *
- * User birthdays do occasionally change, so we have to modify the server to invalidate this
- * cache when necessary. That invalidation code looks like this:
- *
- * <pre>
- * public class UserBirthdayServiceImpl {
- *   ...
- *   public UserBirthdayServiceImpl() {
- *     ...
- *     ActivityThread.currentActivityThread().disableUserBirthdayCache();
- *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
- *   }
- *
- *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
- *     mUidToBirthday.clear();
- *     mUidToBirthday.putAll(uidToBirthday);
- *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
- *   }
- *   ...
- * }
- * </pre>
- *
- * The call to {@code PropertyInvalidatedCache.invalidateCache()} guarantees that all clients
- * will re-fetch birthdays from binder during consequent calls to
- * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
- * held, we maintain consistency between different client views of the birthday state. The use
- * of PropertyInvalidatedCache in this idiomatic way introduces no new race conditions.
- *
- * PropertyInvalidatedCache has a few other features for doing things like incremental
- * enhancement of cached values and invalidation of multiple caches (that all share the same
- * property key) at once.
- *
- * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
- * time we update the cache. SELinux configuration must allow everyone to read this property
- * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
- * the property. (These properties conventionally begin with the "cache_key." prefix.)
- *
- * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
- * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In
- * this local case, there's no IPC, so use of the cache is (depending on exact
- * circumstance) unnecessary.
- *
- * There may be queries for which it is more efficient to bypass the cache than to cache
- * the result.  This would be true, for example, if some queries would require frequent
- * cache invalidation while other queries require infrequent invalidation.  To expand on
- * the birthday example, suppose that there is a userId that signifies "the next
- * birthday".  When passed this userId, the server returns the next birthday among all
- * users - this value changes as time advances.  The userId value can be cached, but the
- * cache must be invalidated whenever a birthday occurs, and this invalidates all
- * birthdays.  If there is a large number of users, invalidation will happen so often that
- * the cache provides no value.
- *
- * The class provides a bypass mechanism to handle this situation.
- * <pre>
- * public class ActivityThread {
- *   ...
- *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
- *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
- *           {@literal @}Override
- *           public Birthday apply(Integer) {
- *              return GetService("birthdayd").getUserBirthday(userId);
- *           }
- *           {@literal @}Override
- *           public boolean shouldBypassQuery(Integer userId) {
- *               return userId == NEXT_BIRTHDAY;
- *           }
- *       };
- *   ...
- * }
- * </pre>
- *
- * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
- * particular query.  The {@code shouldBypassQuery()} method is not abstract and the default
- * implementation returns false.
- *
- * For security, there is a allowlist of processes that are allowed to invalidate a cache.
- * The allowlist includes normal runtime processes but does not include test processes.
- * Test processes must call {@code PropertyInvalidatedCache.disableForTestMode()} to disable
- * all cache activity in that process.
- *
- * Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
- *
- * To test a binder cache, create one or more tests that exercise the binder method.  This
- * should be done twice: once with production code and once with a special image that sets
- * {@code DEBUG} and {@code VERIFY} true.  In the latter case, verify that no cache
- * inconsistencies are reported.  If a cache inconsistency is reported, however, it might be a
- * false positive.  This happens if the server side data can be read and written non-atomically
- * with respect to cache invalidation.
+ * This interface is deprecated.  New clients should use {@link IpcDataCache} instead.  Internally,
+ * that class uses {@link PropertyInvalidatedCache} , but that design may change in the future.
  *
  * @param <Query> The class used to index cache entries: must be hashable and comparable
  * @param <Result> The class holding cache entries; use a boxed primitive if possible
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 03bec71..c13a58f 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+import static android.server.Flags.removeGameManagerServiceFromWear;
 
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
@@ -74,6 +75,7 @@
 import android.companion.virtual.VirtualDeviceManager;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.EnabledSince;
 import android.content.ClipboardManager;
 import android.content.ContentCaptureOptions;
@@ -309,6 +311,16 @@
     static final long ENABLE_CHECKING_TELEPHONY_FEATURES_FOR_VCN = 330902016;
 
     /**
+     * After {@link Build.VERSION_CODES.VANILLA_ICE_CREAM}, Wear devices will be allowed to publish
+     * no {@link GameManager} instance. This is because the respective system service is no longer
+     * started for Wear devices given that the applications of the service do not currently apply to
+     * Wear.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    static final long NULL_GAME_MANAGER_IN_WEAR = 340929737;
+
+    /**
      * The corresponding vendor API for Android V
      *
      * <p>Starting with Android V, the vendor API format has switched to YYYYMM.
@@ -1624,8 +1636,24 @@
                     @Override
                     public GameManager createService(ContextImpl ctx)
                             throws ServiceNotFoundException {
-                        return new GameManager(ctx.getOuterContext(),
-                                ctx.mMainThread.getHandler());
+                        final PackageManager pm = ctx.getPackageManager();
+                        final boolean isWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+                        final IBinder binder =
+                                // Allow a potentially absent GameManagerService only for
+                                // Wear devices. For non-Wear devices, throw a
+                                // ServiceNotFoundException when the service is missing.
+                                (removeGameManagerServiceFromWear() && isWatch)
+                                        ? ServiceManager.getService(Context.GAME_SERVICE)
+                                        : ServiceManager.getServiceOrThrow(Context.GAME_SERVICE);
+
+                        if (binder == null
+                                && Compatibility.isChangeEnabled(NULL_GAME_MANAGER_IN_WEAR)) {
+                            return null;
+                        }
+
+                        return new GameManager(
+                                ctx.getOuterContext(),
+                                IGameManagerService.Stub.asInterface(binder));
                     }
                 });
 
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 5ed1f4e..637187e 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -177,6 +177,10 @@
         {
             "file_patterns": ["(/|^)AppOpsManager.java"],
             "name": "CtsAppOpsTestCases"
+        },
+        {
+            "file_patterns": ["(/|^)BroadcastStickyCache.java"],
+            "name": "BroadcastUnitTests"
         }
     ]
 }
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 38bd576..1f31ab5 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -147,3 +147,21 @@
          purpose: PURPOSE_BUGFIX
      }
 }
+
+flag {
+     namespace: "backstage_power"
+     name: "use_sticky_bcast_cache"
+     description: "Use cache for sticky broadcast intents"
+     is_fixed_read_only: true
+     bug: "356148006"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
+
+flag {
+     namespace: "system_performance"
+     name: "app_start_info_component"
+     description: "Control ApplicationStartInfo component field and API"
+     bug: "362537357"
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index daa15f0..9be928f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -213,19 +213,17 @@
  * <a href="{@docRoot}guide/topics/admin/device-admin.html">Device Administration</a>
  * developer guide.
  *
- * <p id="devicepolicycontroller">Through <a href="#managed_provisioning">Managed Provisioning</a>,
- * Device Administrator apps can also be recognised as <b>
- Device Policy Controllers</b>. Device Policy Controllers can be one of
+ * <p id="devicepolicycontroller">Device Administrator apps can also be recognised as <b>
+ * Device Policy Controllers</b>. Device Policy Controllers can be one of
  * two types:
  * <ul>
  * <li>A <i id="deviceowner">Device Owner</i>, which only ever exists on the
- * {@link UserManager#isSystemUser System User} or {@link UserManager#isMainUser Main User}, is
+ * {@link UserManager#isSystemUser System User} or Main User, is
  * the most powerful type of Device Policy Controller and can affect policy across the device.
  * <li>A <i id="profileowner">Profile Owner<i>, which can exist on any user, can
  * affect policy on the user it is on, and when it is running on
  * {@link UserManager#isProfile a profile} has
- * <a href="#profile-on-parent">limited</a> ability to affect policy on its
- * {@link UserManager#getProfileParent parent}.
+ * <a href="#profile-on-parent">limited</a> ability to affect policy on its parent.
  * </ul>
  *
  * <p>Additional capabilities can be provided to Device Policy Controllers in
@@ -233,7 +231,7 @@
  * <ul>
  * <li>A Profile Owner on an <a href="#organization-owned">organization owned</a> device has access
  * to additional abilities, both <a href="#profile-on-parent-organization-owned">affecting policy on the profile's</a>
- * {@link UserManager#getProfileParent parent} and also the profile itself.
+ * parent and also the profile itself.
  * <li>A Profile Owner running on the {@link UserManager#isSystemUser System User} has access to
  * additional capabilities which affect the {@link UserManager#isSystemUser System User} and
  * also the whole device.
@@ -245,13 +243,12 @@
  * Controller</a>.
  *
  * <p><a href="#permissions">Permissions</a> are generally only given to apps
- * fulfilling particular key roles on the device (such as managing {@link DeviceLockManager
-device locks}).
+ * fulfilling particular key roles on the device (such as managing
+ * {@link android.devicelock.DeviceLockManager device locks}).
  *
  * <p id="roleholder"><b>Device Policy Management Role Holder</b>
- * <p>One app on the device fulfills the {@link RoleManager#ROLE_DEVICE_POLICY_MANAGEMENT Device
-Policy Management Role} and is trusted with managing the overall state of
- * Device Policy. This has access to much more powerful methods than
+ * <p>One app on the device fulfills the Device Policy Management Role and is trusted with managing
+ * the overall state of Device Policy. This has access to much more powerful methods than
  * <a href="#managingapps">managing apps</a>.
  *
  * <p id="querying"><b>Querying Device Policy</b>
@@ -273,7 +270,7 @@
  *
  * <p id="managed_profile">A <b>Managed Profile</b> enables data separation. For example to use
  * a device both for personal and corporate usage. The managed profile and its
- * {@link UserManager#getProfileParent parent} share a launcher.
+ * parent share a launcher.
  *
  * <p id="affiliated"><b>Affiliation</b>
  * <p>Using the {@link #setAffiliationIds} method, a
@@ -6643,7 +6640,7 @@
      * @param flags May be 0 or {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY}.
      * @throws SecurityException if the calling application does not own an active administrator
      *             that uses {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} and the does not hold
-     *             the {@link android.Manifest.permission#LOCK_DEVICE} permission, or
+     *             the LOCK_DEVICE permission, or
      *             the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is passed by an
      *             application that is not a profile owner of a managed profile.
      * @throws IllegalArgumentException if the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 4682f3d..216ba5d 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -27,6 +27,8 @@
 import android.annotation.SystemService;
 import android.annotation.UserHandleAware;
 import android.content.Context;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
 
 import java.util.Objects;
@@ -73,7 +75,43 @@
      *     android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
      *     android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
      *     ExecuteAppFunctionResponse.RESULT_DENIED}.
+     * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor,
+     *     CancellationSignal, Consumer)} instead. This method will be removed once usage references
+     *     are updated.
      */
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+                Manifest.permission.EXECUTE_APP_FUNCTIONS
+            },
+            conditional = true)
+    @UserHandleAware
+    @Deprecated
+    public void executeAppFunction(
+            @NonNull ExecuteAppFunctionRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+        executeAppFunction(request, executor, new CancellationSignal(), callback);
+    }
+
+    /**
+     * 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}.
+     *
+     * @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. 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
+     *     ExecuteAppFunctionResponse.RESULT_DENIED}.
+     */
+    // TODO(b/357551503): Document the behavior when the cancellation signal is issued.
     // TODO(b/360864791): Document that apps can opt-out from being executed by callers with
     //   EXECUTE_APP_FUNCTIONS and how a caller knows whether a function is opted out.
     // TODO(b/357551503): Update documentation when get / set APIs are implemented that this will
@@ -88,6 +126,7 @@
     public void executeAppFunction(
             @NonNull ExecuteAppFunctionRequest request,
             @NonNull @CallbackExecutor Executor executor,
+            @NonNull CancellationSignal cancellationSignal,
             @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
         Objects.requireNonNull(request);
         Objects.requireNonNull(executor);
@@ -96,25 +135,31 @@
         ExecuteAppFunctionAidlRequest aidlRequest =
                 new ExecuteAppFunctionAidlRequest(
                         request, mContext.getUser(), mContext.getPackageName());
+
         try {
-            mService.executeAppFunction(
-                    aidlRequest,
-                    new IExecuteAppFunctionCallback.Stub() {
-                        @Override
-                        public void onResult(ExecuteAppFunctionResponse result) {
-                            try {
-                                executor.execute(() -> callback.accept(result));
-                            } catch (RuntimeException e) {
-                                // Ideally shouldn't happen since errors are wrapped into the
-                                // response, but we catch it here for additional safety.
-                                callback.accept(
-                                        ExecuteAppFunctionResponse.newFailure(
-                                                getResultCode(e),
-                                                e.getMessage(),
-                                                /* extras= */ null));
-                            }
-                        }
-                    });
+            ICancellationSignal cancellationTransport =
+                    mService.executeAppFunction(
+                            aidlRequest,
+                            new IExecuteAppFunctionCallback.Stub() {
+                                @Override
+                                public void onResult(ExecuteAppFunctionResponse result) {
+                                    try {
+                                        executor.execute(() -> callback.accept(result));
+                                    } catch (RuntimeException e) {
+                                        // Ideally shouldn't happen since errors are wrapped into
+                                        // the
+                                        // response, but we catch it here for additional safety.
+                                        callback.accept(
+                                                ExecuteAppFunctionResponse.newFailure(
+                                                        getResultCode(e),
+                                                        e.getMessage(),
+                                                        /* extras= */ null));
+                                    }
+                                }
+                            });
+            if (cancellationTransport != null) {
+                cancellationSignal.setRemote(cancellationTransport);
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index 36daaab..83b5aa0 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -164,7 +164,13 @@
      */
     @Nullable
     public Boolean getEnabled() {
-        return (Boolean) getProperty(PROPERTY_ENABLED);
+        // We can't use getPropertyBoolean here. getPropertyBoolean returns false instead of null
+        // if the value is missing.
+        boolean[] enabled = getPropertyBooleanArray(PROPERTY_ENABLED);
+        if (enabled == null || enabled.length == 0) {
+            return null;
+        }
+        return enabled[0];
     }
 
     /** Returns the qualified id linking to the static metadata of the app function. */
@@ -201,11 +207,16 @@
         /**
          * Sets an indicator specifying if the function is enabled or not. This would override the
          * default enabled state in the static metadata ({@link
-         * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}).
+         * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}). Sets this to
+         * null to clear the override.
          */
         @NonNull
-        public Builder setEnabled(boolean enabled) {
-            setPropertyBoolean(PROPERTY_ENABLED, enabled);
+        public Builder setEnabled(@Nullable Boolean enabled) {
+            if (enabled == null) {
+                setPropertyBoolean(PROPERTY_ENABLED);
+            } else {
+                setPropertyBoolean(PROPERTY_ENABLED, enabled);
+            }
             return this;
         }
 
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 0d981ea..8e41773 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -29,7 +29,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.CancellationSignal;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
 
 import java.util.function.Consumer;
 
@@ -74,6 +79,7 @@
          */
         void perform(
                 @NonNull ExecuteAppFunctionRequest request,
+                @NonNull CancellationSignal cancellationSignal,
                 @NonNull Consumer<ExecuteAppFunctionResponse> callback);
     }
 
@@ -85,6 +91,7 @@
             @Override
             public void executeAppFunction(
                     @NonNull ExecuteAppFunctionRequest request,
+                    @NonNull ICancellationCallback cancellationCallback,
                     @NonNull IExecuteAppFunctionCallback callback) {
                 if (context.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
                         == PERMISSION_DENIED) {
@@ -93,7 +100,10 @@
                 SafeOneTimeExecuteAppFunctionCallback safeCallback =
                         new SafeOneTimeExecuteAppFunctionCallback(callback);
                 try {
-                    onExecuteFunction.perform(request, safeCallback::onResult);
+                    onExecuteFunction.perform(
+                            request,
+                            buildCancellationSignal(cancellationCallback),
+                            safeCallback::onResult);
                 } catch (Exception ex) {
                     // Apps should handle exceptions. But if they don't, report the error on
                     // behalf of them.
@@ -105,6 +115,21 @@
         };
     }
 
+    private static CancellationSignal buildCancellationSignal(
+            @NonNull ICancellationCallback cancellationCallback) {
+        final ICancellationSignal cancellationSignalTransport =
+                CancellationSignal.createTransport();
+        CancellationSignal cancellationSignal =
+                CancellationSignal.fromTransport(cancellationSignalTransport);
+        try {
+            cancellationCallback.sendCancellationTransport(cancellationSignalTransport);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return cancellationSignal ;
+    }
+
     private final Binder mBinder = createBinder(
             AppFunctionService.this,
             AppFunctionService.this::onExecuteFunction);
@@ -115,6 +140,7 @@
         return mBinder;
     }
 
+
     /**
      * Called by the system to execute a specific app function.
      *
@@ -134,9 +160,45 @@
      *
      * @param request The function execution request.
      * @param callback A callback to report back the result.
+     *
+     * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal,
+     *     Consumer)} instead. This method will be removed once usage references are updated.
      */
     @MainThread
+    @Deprecated
     public abstract void onExecuteFunction(
             @NonNull ExecuteAppFunctionRequest request,
             @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+
+    /**
+     * Called by the system to execute a specific app function.
+     *
+     * <p>This method is triggered when the system requests your AppFunctionService to handle a
+     * particular function you have registered and made available.
+     *
+     * <p>To ensure proper routing of function requests, assign a unique identifier to each
+     * function. This identifier doesn't need to be globally unique, but it must be unique within
+     * your app. For example, a function to order food could be identified as "orderFood". In most
+     * cases this identifier should come from the ID automatically generated by the AppFunctions
+     * SDK. You can determine the specific function to invoke by calling {@link
+     * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
+     *
+     * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
+     * thread and dispatch the result with the given callback. You should always report back the
+     * result using the callback, no matter if the execution was successful or not.
+     *
+     * <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel
+     * the execution of function if requested by the system.
+     *
+     * @param request The function execution request.
+     * @param cancellationSignal A signal to cancel the execution.
+     * @param callback A callback to report back the result.
+     */
+    @MainThread
+    public void onExecuteFunction(
+            @NonNull ExecuteAppFunctionRequest request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+        onExecuteFunction(request, callback);
+    }
 }
diff --git a/core/java/android/app/appfunctions/GenericDocumentWrapper.java b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
index 84b1837..b29b64e 100644
--- a/core/java/android/app/appfunctions/GenericDocumentWrapper.java
+++ b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
@@ -16,10 +16,13 @@
 
 package android.app.appfunctions;
 
+import android.annotation.Nullable;
 import android.app.appsearch.GenericDocument;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.MathUtils;
 
+import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 
 import java.util.Objects;
@@ -31,24 +34,33 @@
  * <p>{#link {@link Parcel#writeBlob(byte[])}} could take care of whether to pass data via binder
  * directly or Android shared memory if the data is large.
  *
+ * <p>This class performs lazy unparcelling. The `GenericDocument` is only unparcelled
+ * from the underlying `Parcel` when {@link #getValue()} is called. This optimization
+ * allows the system server to pass through the generic document, without unparcel and parcel it.
+ *
  * @hide
  * @see Parcel#writeBlob(byte[])
  */
 public final class GenericDocumentWrapper implements Parcelable {
+    @Nullable
+    @GuardedBy("mLock")
+    private GenericDocument mGenericDocument;
+    @GuardedBy("mLock")
+    @Nullable private Parcel mParcel;
+    private final Object mLock = new Object();
+
     public static final Creator<GenericDocumentWrapper> CREATOR =
             new Creator<>() {
                 @Override
                 public GenericDocumentWrapper createFromParcel(Parcel in) {
-                    byte[] dataBlob = Objects.requireNonNull(in.readBlob());
-                    Parcel unmarshallParcel = Parcel.obtain();
-                    try {
-                        unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length);
-                        unmarshallParcel.setDataPosition(0);
-                        return new GenericDocumentWrapper(
-                                GenericDocument.createFromParcel(unmarshallParcel));
-                    } finally {
-                        unmarshallParcel.recycle();
-                    }
+                    int length = in.readInt();
+                    int offset = in.dataPosition();
+                    in.setDataPosition(MathUtils.addOrThrow(offset, length));
+
+                    Parcel p = Parcel.obtain();
+                    p.appendFrom(in, offset, length);
+                    p.setDataPosition(0);
+                    return new GenericDocumentWrapper(p);
                 }
 
                 @Override
@@ -56,16 +68,42 @@
                     return new GenericDocumentWrapper[size];
                 }
             };
-    @NonNull private final GenericDocument mGenericDocument;
 
     public GenericDocumentWrapper(@NonNull GenericDocument genericDocument) {
         mGenericDocument = Objects.requireNonNull(genericDocument);
+        mParcel = null;
+    }
+
+    public GenericDocumentWrapper(@NonNull Parcel parcel) {
+        mGenericDocument = null;
+        mParcel = Objects.requireNonNull(parcel);
     }
 
     /** Returns the wrapped {@link android.app.appsearch.GenericDocument} */
     @NonNull
     public GenericDocument getValue() {
-        return mGenericDocument;
+        unparcel();
+        synchronized (mLock) {
+            return Objects.requireNonNull(mGenericDocument);
+        }
+    }
+
+    private void unparcel() {
+        synchronized (mLock) {
+            if (mGenericDocument != null) {
+                return;
+            }
+            byte[] dataBlob = Objects.requireNonNull(Objects.requireNonNull(mParcel).readBlob());
+            Parcel unmarshallParcel = Parcel.obtain();
+            try {
+                unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length);
+                unmarshallParcel.setDataPosition(0);
+                mGenericDocument = GenericDocument.createFromParcel(unmarshallParcel);
+                mParcel = null;
+            } finally {
+                unmarshallParcel.recycle();
+            }
+        }
     }
 
     @Override
@@ -75,13 +113,32 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        Parcel parcel = Parcel.obtain();
-        try {
-            mGenericDocument.writeToParcel(parcel, flags);
-            byte[] bytes = parcel.marshall();
-            dest.writeBlob(bytes);
-        } finally {
-            parcel.recycle();
+        synchronized (mLock) {
+            if (mGenericDocument != null) {
+                int lengthPos = dest.dataPosition();
+                // write a placeholder for length
+                dest.writeInt(-1);
+                Parcel tempParcel = Parcel.obtain();
+                byte[] bytes;
+                try {
+                    mGenericDocument.writeToParcel(tempParcel, flags);
+                    bytes = tempParcel.marshall();
+                } finally {
+                    tempParcel.recycle();
+                }
+                int startPos = dest.dataPosition();
+                dest.writeBlob(bytes);
+                int endPos = dest.dataPosition();
+                dest.setDataPosition(lengthPos);
+                // Overwrite the length placeholder
+                dest.writeInt(endPos - startPos);
+                dest.setDataPosition(endPos);
+
+            } else {
+                Parcel originalParcel = Objects.requireNonNull(mParcel);
+                dest.writeInt(originalParcel.dataSize());
+                dest.appendFrom(originalParcel, 0, originalParcel.dataSize());
+            }
         }
     }
 }
diff --git a/core/java/android/app/appfunctions/IAppFunctionManager.aidl b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
index 28827bb..c63217f 100644
--- a/core/java/android/app/appfunctions/IAppFunctionManager.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
@@ -18,6 +18,7 @@
 
 import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
 import android.app.appfunctions.IExecuteAppFunctionCallback;
+import android.os.ICancellationSignal;
 
 /**
  * Defines the interface for apps to interact with the app function execution service
@@ -32,8 +33,8 @@
     * @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)")
-    void executeAppFunction(
+    ICancellationSignal executeAppFunction(
         in ExecuteAppFunctionAidlRequest request,
         in IExecuteAppFunctionCallback callback
     );
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/appfunctions/IAppFunctionService.aidl b/core/java/android/app/appfunctions/IAppFunctionService.aidl
index cc5a20c..291f33c 100644
--- a/core/java/android/app/appfunctions/IAppFunctionService.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionService.aidl
@@ -16,7 +16,7 @@
 
 package android.app.appfunctions;
 
-import android.os.Bundle;
+import android.app.appfunctions.ICancellationCallback;
 import android.app.appfunctions.IExecuteAppFunctionCallback;
 import android.app.appfunctions.ExecuteAppFunctionRequest;
 
@@ -34,10 +34,12 @@
      * Called by the system to execute a specific app function.
      *
      * @param request  the function execution request.
+     * @param cancellationCallback a callback to send back the cancellation transport.
      * @param callback a callback to report back the result.
      */
     void executeAppFunction(
         in ExecuteAppFunctionRequest request,
+        in ICancellationCallback cancellationCallback,
         in IExecuteAppFunctionCallback callback
     );
 }
diff --git a/core/java/android/app/appfunctions/ICancellationCallback.aidl b/core/java/android/app/appfunctions/ICancellationCallback.aidl
new file mode 100644
index 0000000..03235ac
--- /dev/null
+++ b/core/java/android/app/appfunctions/ICancellationCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.app.appfunctions;
+
+import android.os.ICancellationSignal;
+
+/** {@hide} */
+oneway interface ICancellationCallback {
+    void sendCancellationTransport(in ICancellationSignal cancellationTransport);
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 9b06adf..b139017 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -31,6 +31,16 @@
 }
 
 flag {
+  name: "modes_ui_empty_shade"
+  namespace: "systemui"
+  description: "Shows mode that is currently blocking notifications in the empty shade; dependent on flags modes_api and modes_ui"
+  bug: "366003631"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "modes_ui_test"
   namespace: "systemui"
   description: "Guards new CTS tests for Modes; dependent on flags modes_api and modes_ui"
@@ -105,6 +115,13 @@
 }
 
 flag {
+  name: "notification_no_custom_view_conversations"
+  namespace: "systemui"
+  description: "Ensures that conversations are not allowed to use Custom Views."
+  bug: "368817201"
+}
+
+flag {
   name: "keyguard_private_notifications"
   namespace: "systemui"
   description: "Fixes the behavior of KeyguardManager#setPrivateNotificationsAllowed()"
@@ -244,4 +261,4 @@
   namespace: "systemui"
   description: "Guards new android.app.richongoingnotification promotion and new uis"
   bug: "337261753"
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
index 8d25cad..4598421 100644
--- a/core/java/android/app/supervision/ISupervisionManager.aidl
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -21,5 +21,5 @@
  * {@hide}
  */
 interface ISupervisionManager {
-    boolean isSupervisionEnabled();
+    boolean isSupervisionEnabledForUser(int userId);
 }
diff --git a/core/java/android/app/supervision/OWNERS b/core/java/android/app/supervision/OWNERS
index afc5495..4785a72 100644
--- a/core/java/android/app/supervision/OWNERS
+++ b/core/java/android/app/supervision/OWNERS
@@ -1,2 +1,2 @@
 jparks@google.com
-romkal@google.com
+vtrmc@google.com
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 8611a92..aee1cd9 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -17,6 +17,7 @@
 package android.app.supervision;
 
 import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.RemoteException;
@@ -45,13 +46,12 @@
      *
      * @hide
      */
+    @UserHandleAware
     public boolean isSupervisionEnabled() {
         try {
-            return mService.isSupervisionEnabled();
+            return mService.isSupervisionEnabledForUser(mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
-
-
 }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index abb562d..d8142fd 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1042,10 +1042,11 @@
     }
 
     /**
-     * Get the available info about the AppWidget.
+     * Returns the {@link AppWidgetProviderInfo} for the specified AppWidget.
      *
-     * @return A appWidgetId.  If the appWidgetId has not been bound to a provider yet, or
-     * you don't have access to that appWidgetId, null is returned.
+     * @return Information regarding the provider of speficied widget, returns null if the
+     *         appWidgetId has not been bound to a provider yet, or you don't have access
+     *         to that widget.
      */
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
         if (mService == null) {
@@ -1390,7 +1391,7 @@
      *
      * @param provider The {@link ComponentName} for the {@link
      *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
-     * @param extras In not null, this is passed to the launcher app. For eg {@link
+     * @param extras IF not null, this is passed to the launcher app. e.g. {@link
      *    #EXTRA_APPWIDGET_PREVIEW} can be used for a custom preview.
      * @param successCallback If not null, this intent will be sent when the widget is created.
      *
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 7117f25..ac9263c 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -63,4 +63,14 @@
   namespace: "app_widgets"
   description: "Remote document support features in Q2 2025 release"
   bug: "339721781"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "security_policy_interact_across_users"
+    namespace: "app_widgets"
+    description: "Allow packages with interact_across_users permission to manage app widgets on behalf of other users."
+    bug: "357621815"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 12c5d07..91f7a8b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4324,6 +4324,7 @@
             SECURITY_STATE_SERVICE,
            //@hide: ECM_ENHANCED_CONFIRMATION_SERVICE,
             CONTACT_KEYS_SERVICE,
+            RANGING_SERVICE,
 
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -6402,6 +6403,17 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.ranging.RangingManager}.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @FlaggedApi(com.android.ranging.flags.Flags.FLAG_RANGING_STACK_ENABLED)
+    @SystemApi
+    public static final String RANGING_SERVICE = "ranging";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.DreamManager} for controlling Dream states.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 031380d..044178c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -20,6 +20,7 @@
 import static android.content.ContentProvider.maybeAddUserId;
 import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 import static android.security.Flags.FLAG_FRP_ENFORCEMENT;
+import static android.security.Flags.preventIntentRedirect;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -7687,9 +7688,17 @@
     /** @hide */
     public static final int LOCAL_FLAG_FROM_SYSTEM = 1 << 5;
 
+    /**
+     * This flag indicates the creator token of this intent has been verified.
+     *
+     * @hide
+     */
+    public static final int LOCAL_FLAG_CREATOR_TOKEN_VERIFIED = 1 << 6;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = {
             EXTENDED_FLAG_FILTER_MISMATCH,
+            EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ExtendedFlags {}
@@ -7703,6 +7712,13 @@
     @TestApi
     public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0;
 
+    /**
+     * This flag indicates the creator token of this intent is either missing or invalid.
+     *
+     * @hide
+     */
+    public static final int EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN = 1 << 1;
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // toUri() and parseUri() options.
@@ -7870,6 +7886,7 @@
         this.mPackage = o.mPackage;
         this.mComponent = o.mComponent;
         this.mOriginalIntent = o.mOriginalIntent;
+        this.mCreatorTokenInfo = o.mCreatorTokenInfo;
 
         if (o.mCategories != null) {
             this.mCategories = new ArraySet<>(o.mCategories);
@@ -12176,6 +12193,60 @@
         return (mExtras != null) ? mExtras.describeContents() : 0;
     }
 
+    private static class CreatorTokenInfo {
+        // Stores a creator token for an intent embedded as an extra intent in a top level intent,
+        private IBinder mCreatorToken;
+        // Stores all extra keys whose values are intents for a top level intent.
+        private ArraySet<String> mExtraIntentKeys;
+    }
+
+    private @Nullable CreatorTokenInfo mCreatorTokenInfo;
+
+    /** @hide */
+    public void removeCreatorTokenInfo() {
+        mCreatorTokenInfo = null;
+    }
+
+    /** @hide */
+    public @Nullable IBinder getCreatorToken() {
+        return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mCreatorToken;
+    }
+
+    /** @hide */
+    public Set<String> getExtraIntentKeys() {
+        return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mExtraIntentKeys;
+    }
+
+    /** @hide */
+    public void setCreatorToken(@NonNull IBinder creatorToken) {
+        if (mCreatorTokenInfo == null) {
+            mCreatorTokenInfo = new CreatorTokenInfo();
+        }
+        mCreatorTokenInfo.mCreatorToken = creatorToken;
+    }
+
+    /**
+     * Collects keys in the extra bundle whose value are intents.
+     * @hide
+     */
+    public void collectExtraIntentKeys() {
+        if (!preventIntentRedirect()) return;
+
+        if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
+            for (String key : mExtras.keySet()) {
+                if (mExtras.get(key) instanceof Intent) {
+                    if (mCreatorTokenInfo == null) {
+                        mCreatorTokenInfo = new CreatorTokenInfo();
+                    }
+                    if (mCreatorTokenInfo.mExtraIntentKeys == null) {
+                        mCreatorTokenInfo.mExtraIntentKeys = new ArraySet<>();
+                    }
+                    mCreatorTokenInfo.mExtraIntentKeys.add(key);
+                }
+            }
+        }
+    }
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeString8(mAction);
         Uri.writeToParcel(out, mData);
@@ -12225,6 +12296,16 @@
         } else {
             out.writeInt(0);
         }
+
+        if (preventIntentRedirect()) {
+            if (mCreatorTokenInfo == null) {
+                out.writeInt(0);
+            } else {
+                out.writeInt(1);
+                out.writeStrongBinder(mCreatorTokenInfo.mCreatorToken);
+                out.writeArraySet(mCreatorTokenInfo.mExtraIntentKeys);
+            }
+        }
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<Intent> CREATOR
@@ -12282,6 +12363,14 @@
         if (in.readInt() != 0) {
             mOriginalIntent = new Intent(in);
         }
+
+        if (preventIntentRedirect()) {
+            if (in.readInt() != 0) {
+                mCreatorTokenInfo = new CreatorTokenInfo();
+                mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
+                mCreatorTokenInfo.mExtraIntentKeys = (ArraySet<String>) in.readArraySet(null);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/content/pm/Android.bp b/core/java/android/content/pm/Android.bp
new file mode 100644
index 0000000..057b5da
--- /dev/null
+++ b/core/java/android/content/pm/Android.bp
@@ -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 {
+    default_team: "trendy_team_framework_android_packages",
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "framework-pm-sources",
+    srcs: [
+        "**/*.java",
+        "**/*.aidl",
+    ],
+    exclude_srcs: [
+        "dex/**/*.java",
+        "overlay/**/*.java",
+        "permission/**/*.java",
+    ],
+    visibility: ["//frameworks/base"],
+}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 5acebf5..d77b2f5 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -94,7 +94,7 @@
     private final String mPath;
     private final String mPackageName;
     private final String mName;
-    private final List<String> mCodePaths;
+    private List<String> mCodePaths;
 
     private final long mVersion;
     private final @Type int mType;
@@ -282,6 +282,15 @@
     }
 
     /**
+     * Sets new all code paths for that library.
+     *
+     * @hide
+     */
+    public void setAllCodePaths(List<String> paths) {
+        mCodePaths = paths;
+    }
+
+    /**
      * Add a library dependency to that library. Note that this
      * should be called under the package manager lock.
      *
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 139ff65..160cbdf 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -293,4 +293,19 @@
     description: "Feature flag to provide the new methods within launcher apps class to get packages."
     bug: "363324203"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    name: "remove_cross_user_permission_hack"
+    namespace: "package_manager_service"
+    description: "Feature flag to remove hack code of using PackageManager.MATCH_ANY_USER flag without cross user permission."
+    bug: "332664521"
+    is_fixed_read_only: true
+}
+
+flag {
+    name: "delete_packages_silently_backport"
+    namespace: "package_manager_service"
+    description: "Feature flag to enable the holder of SYSTEM_APP_PROTECTION_SERVICE role to silently delete packages. To be deprecated by delete_packages_silently."
+    bug: "361776825"
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 34f3b61..fd1a896 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -221,6 +221,17 @@
 }
 
 flag {
+    name: "cache_user_properties_correctly_read_only"
+    namespace: "multiuser"
+    description: "UserProperties cache needs to take into account who the callingUid is."
+    bug: "369198539"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+  is_fixed_read_only: true
+}
+
+flag {
     name: "cache_user_serial_number"
     namespace: "multiuser"
     description: "Optimise user serial number retrieval"
diff --git a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
index a269072..bb4a0e0 100644
--- a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
+++ b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
@@ -104,6 +104,11 @@
                 sCallingGlobalSettings = true;
                 flags = Settings.Global.getString(app.getContentResolver(),
                         Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS);
+            } catch (Exception e) {
+                // The process is unable to read the flags.  Treat this condition the same as if
+                // the ActivityThread application was not available.
+                Log.w(TAG, "Cannot read global setting "
+                        + Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS + " - " + e.toString());
             } finally {
                 sCallingGlobalSettings = false;
             }
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 9b87df9..3cf508a 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -259,10 +259,8 @@
         private static final String PROXY_SERVICE_NAME =
                 "com.android.cameraextensions.CameraExtensionsProxyService";
 
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         private static final int FALLBACK_PACKAGE_NAME =
                 com.android.internal.R.string.config_extensionFallbackPackageName;
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         private static final int FALLBACK_SERVICE_NAME =
                 com.android.internal.R.string.config_extensionFallbackServiceName;
 
@@ -309,7 +307,7 @@
                   intent.setClassName(vendorProxyPackage, vendorProxyService);
                 }
 
-                if (Flags.concertMode() && useFallback) {
+                if (useFallback) {
                     String packageName = ctx.getResources().getString(FALLBACK_PACKAGE_NAME);
                     String serviceName = ctx.getResources().getString(FALLBACK_SERVICE_NAME);
 
@@ -433,7 +431,7 @@
                     releaseProxyConnectionLocked(ctx, extension);
                 }
 
-                if (Flags.concertMode() && ret && useFallback && mIsFallbackEnabled) {
+                if (ret && useFallback && mIsFallbackEnabled) {
                     try {
                         InitializeSessionHandler cb = new InitializeSessionHandler(ctx);
                         initializeSession(cb, extension);
@@ -462,26 +460,24 @@
 
             boolean ret = registerClientHelper(ctx, token, extension, false /*useFallback*/);
 
-            if (Flags.concertMode()) {
-                // Check if user enabled fallback impl
-                ContentResolver resolver = ctx.getContentResolver();
-                int userEnabled = Settings.Secure.getInt(resolver,
-                        Settings.Secure.CAMERA_EXTENSIONS_FALLBACK, 1);
+            // Check if user enabled fallback impl
+            ContentResolver resolver = ctx.getContentResolver();
+            int userEnabled = Settings.Secure.getInt(resolver,
+                    Settings.Secure.CAMERA_EXTENSIONS_FALLBACK, 1);
 
-                boolean vendorImpl = true;
-                if (ret && (mConnectionManager.getProxy(extension) != null) && (userEnabled == 1)) {
-                    // At this point, we are connected to either CameraExtensionsProxyService or
-                    // the vendor extension proxy service. If the vendor does not support the
-                    // extension, unregisterClient and re-register client with the proxy service
-                    // containing the fallback impl
-                    vendorImpl = isExtensionSupported(cameraId, extension,
-                            characteristicsMapNative);
-                }
+            boolean vendorImpl = true;
+            if (ret && (mConnectionManager.getProxy(extension) != null) && (userEnabled == 1)) {
+                // At this point, we are connected to either CameraExtensionsProxyService or
+                // the vendor extension proxy service. If the vendor does not support the
+                // extension, unregisterClient and re-register client with the proxy service
+                // containing the fallback impl
+                vendorImpl = isExtensionSupported(cameraId, extension,
+                        characteristicsMapNative);
+            }
 
-                if (!vendorImpl) {
-                    unregisterClient(ctx, token, extension);
-                    ret = registerClientHelper(ctx, token, extension, true /*useFallback*/);
-                }
+            if (!vendorImpl) {
+                unregisterClient(ctx, token, extension);
+                ret = registerClientHelper(ctx, token, extension, true /*useFallback*/);
             }
 
             return ret;
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index 2d9433e..20f89a5 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -156,7 +156,6 @@
          * @see #capture
          * @see #setRepeatingRequest
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         public void onCaptureFailed(@NonNull CameraExtensionSession session,
                 @NonNull CaptureRequest request, @CaptureFailure.FailureReason int failure) {
             // default empty implementation
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index d40b2e3..1b21bdf 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -181,7 +181,7 @@
      * @hide
      */
     @TestApi
-    @FlaggedApi(com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public static final int ROTATION_OVERRIDE_NONE = ICameraService.ROTATION_OVERRIDE_NONE;
 
     /**
@@ -191,7 +191,7 @@
      * @hide
      */
     @TestApi
-    @FlaggedApi(com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public static final int ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT =
             ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT;
 
@@ -201,7 +201,7 @@
      * @hide
      */
     @TestApi
-    @FlaggedApi(com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public static final int ROTATION_OVERRIDE_ROTATION_ONLY =
             ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY;
 
@@ -818,7 +818,7 @@
             }
 
             boolean hasConcurrentStreams =
-                    CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId,
+                    CameraManagerGlobal.get().cameraIdHasConcurrentStreams(cameraId,
                             mContext.getDeviceId(), getDevicePolicyFromContext(mContext));
             metadata.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
 
@@ -1562,7 +1562,7 @@
      */
     public static int getRotationOverride(@Nullable Context context,
             @Nullable PackageManager packageManager, @Nullable String packageName) {
-        if (com.android.window.flags.Flags.cameraCompatForFreeform()) {
+        if (com.android.window.flags.Flags.enableCameraCompatForDesktopWindowing()) {
             return getRotationOverrideInternal(context, packageManager, packageName);
         } else {
             return shouldOverrideToPortrait(packageManager, packageName)
@@ -1574,7 +1574,7 @@
     /**
      * @hide
      */
-    @FlaggedApi(com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @TestApi
     public static int getRotationOverrideInternal(@Nullable Context context,
             @Nullable PackageManager packageManager, @Nullable String packageName) {
@@ -2629,24 +2629,26 @@
          * @return Whether the camera device was found in the set of combinations returned by
          *         getConcurrentCameraIds
          */
-        public boolean cameraIdHasConcurrentStreamsLocked(String cameraId, int deviceId,
+        public boolean cameraIdHasConcurrentStreams(String cameraId, int deviceId,
                 int devicePolicy) {
-            DeviceCameraInfo info = new DeviceCameraInfo(cameraId,
-                    devicePolicy == DEVICE_POLICY_DEFAULT ? DEVICE_ID_DEFAULT : deviceId);
-            if (!mDeviceStatus.containsKey(info)) {
-                // physical camera ids aren't advertised in concurrent camera id combinations.
-                if (DEBUG) {
-                    Log.v(TAG, " physical camera id " + cameraId + " is hidden." +
-                            " Available logical camera ids : " + mDeviceStatus);
+            synchronized (mLock) {
+                DeviceCameraInfo info = new DeviceCameraInfo(cameraId,
+                        devicePolicy == DEVICE_POLICY_DEFAULT ? DEVICE_ID_DEFAULT : deviceId);
+                if (!mDeviceStatus.containsKey(info)) {
+                    // physical camera ids aren't advertised in concurrent camera id combinations.
+                    if (DEBUG) {
+                        Log.v(TAG, " physical camera id " + cameraId + " is hidden."
+                                + " Available logical camera ids : " + mDeviceStatus);
+                    }
+                    return false;
+                }
+                for (Set<DeviceCameraInfo> comb : mConcurrentCameraIdCombinations) {
+                    if (comb.contains(info)) {
+                        return true;
+                    }
                 }
                 return false;
             }
-            for (Set<DeviceCameraInfo> comb : mConcurrentCameraIdCombinations) {
-                if (comb.contains(info)) {
-                    return true;
-                }
-            }
-            return false;
         }
 
         public void setTorchMode(
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 75d617c..a18a634 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -5293,7 +5293,6 @@
     @PublicKey
     @NonNull
     @SyntheticKey
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public static final Key<android.hardware.camera2.params.LensIntrinsicsSample[]> STATISTICS_LENS_INTRINSICS_SAMPLES =
             new Key<android.hardware.camera2.params.LensIntrinsicsSample[]>("android.statistics.lensIntrinsicsSamples", android.hardware.camera2.params.LensIntrinsicsSample[].class);
 
@@ -5307,7 +5306,6 @@
      * @see CaptureResult#SENSOR_TIMESTAMP
      * @hide
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public static final Key<long[]> STATISTICS_LENS_INTRINSIC_TIMESTAMPS =
             new Key<long[]>("android.statistics.lensIntrinsicTimestamps", long[].class);
 
@@ -5323,7 +5321,6 @@
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      * @hide
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public static final Key<float[]> STATISTICS_LENS_INTRINSIC_SAMPLES =
             new Key<float[]>("android.statistics.lensIntrinsicSamples", float[].class);
 
@@ -5814,7 +5811,6 @@
      */
     @PublicKey
     @NonNull
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public static final Key<android.graphics.Rect> LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION =
             new Key<android.graphics.Rect>("android.logicalMultiCamera.activePhysicalSensorCropRegion", android.graphics.Rect.class);
 
diff --git a/core/java/android/hardware/camera2/extension/AdvancedExtender.java b/core/java/android/hardware/camera2/extension/AdvancedExtender.java
index 8fa09a8..df66f59 100644
--- a/core/java/android/hardware/camera2/extension/AdvancedExtender.java
+++ b/core/java/android/hardware/camera2/extension/AdvancedExtender.java
@@ -53,7 +53,6 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public abstract class AdvancedExtender {
     private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
     private final CameraManager mCameraManager;
@@ -66,7 +65,6 @@
      *
      * @param cameraManager the system camera manager
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public AdvancedExtender(@NonNull CameraManager cameraManager) {
         mCameraManager = cameraManager;
         try {
@@ -101,7 +99,6 @@
      * @param cameraId           The camera2 id string of the camera.
      * @return the camera metadata vendor Id associated with the given camera
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public long getMetadataVendorId(@NonNull String cameraId) {
         long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ?
                 mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
@@ -123,7 +120,6 @@
      *                           CameraCharacteristics.
      * @return true if the extension is supported, otherwise false
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract boolean isExtensionAvailable(@NonNull String cameraId,
             @NonNull CharacteristicsMap charsMap);
 
@@ -144,7 +140,6 @@
      *                           physical camera ids and their
      *                           CameraCharacteristics.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract void initialize(@NonNull String cameraId, @NonNull CharacteristicsMap map);
 
     /**
@@ -159,7 +154,6 @@
      * be identical to the supported preview output format returned here.
      * @param cameraId           The camera2 id string of the camera.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public abstract Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(
             @NonNull String cameraId);
@@ -179,7 +173,6 @@
      * writes the output to the output surface.
      * @param cameraId           The camera2 id string of the camera.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public abstract Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(
             @NonNull String cameraId);
@@ -189,7 +182,6 @@
      * implements all the interactions required for starting an extension
      * and cleanup.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public abstract SessionProcessor getSessionProcessor();
 
@@ -227,7 +219,6 @@
      * @return The list of supported orthogonal capture keys, or empty
      * list if no capture settings are not supported.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public abstract List<CaptureRequest.Key> getAvailableCaptureRequestKeys(
             @NonNull String cameraId);
@@ -245,7 +236,6 @@
      * @return The list of supported capture result keys, or
      * empty list if capture results are not supported.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public abstract List<CaptureResult.Key> getAvailableCaptureResultKeys(
             @NonNull String cameraId);
@@ -270,7 +260,6 @@
      * {@link CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP} and
      *  {@link CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP}.
      */
-    @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
     @NonNull
     public abstract List<Pair<CameraCharacteristics.Key, Object>>
             getAvailableCharacteristicsKeyValues();
@@ -377,7 +366,6 @@
             return false;
         }
 
-        @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
         @Override
         public CameraMetadataNative getAvailableCharacteristicsKeyValues(String cameraId) {
             List<Pair<CameraCharacteristics.Key, Object>> entries =
diff --git a/core/java/android/hardware/camera2/extension/CameraExtensionService.java b/core/java/android/hardware/camera2/extension/CameraExtensionService.java
index 01698d5..9cc4f16 100644
--- a/core/java/android/hardware/camera2/extension/CameraExtensionService.java
+++ b/core/java/android/hardware/camera2/extension/CameraExtensionService.java
@@ -42,7 +42,6 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public abstract class CameraExtensionService extends Service {
     private static final String TAG = "CameraExtensionService";
     private CameraUsageTracker mCameraUsageTracker;
@@ -87,10 +86,8 @@
         }
     };
 
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     protected CameraExtensionService() { }
 
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @Override
     @NonNull
     public final IBinder onBind(@Nullable Intent intent) {
@@ -186,7 +183,6 @@
      *                           unexpectedly.
      * @return true if the registration is successful, false otherwise
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract boolean onRegisterClient(@NonNull IBinder token);
 
     /**
@@ -194,7 +190,6 @@
      *
      * @param token              Binder token
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract void onUnregisterClient(@NonNull IBinder token);
 
     /**
@@ -204,7 +199,6 @@
      *                      extension type
      * @return Valid advanced extender of the requested type
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public abstract AdvancedExtender onInitializeAdvancedExtension(@Extension int extensionType);
 }
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java
index 32139b8..e8b20e3 100644
--- a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java
+++ b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java
@@ -42,11 +42,9 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public final class CameraOutputSurface {
     private final OutputSurface mOutputSurface;
 
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     CameraOutputSurface(@NonNull OutputSurface surface) {
        mOutputSurface = surface;
     }
@@ -59,7 +57,6 @@
      * @param size         Requested size of the camera
      *                     output
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public CameraOutputSurface(@NonNull Surface surface,
             @NonNull Size size) {
         mOutputSurface = new OutputSurface();
@@ -75,7 +72,6 @@
     /**
      * Return the current output {@link Surface}
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public Surface getSurface() {
         return mOutputSurface.surface;
@@ -84,7 +80,6 @@
     /**
      * Return the current requested output size
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public android.util.Size getSize() {
         if (mOutputSurface.size != null) {
@@ -96,7 +91,6 @@
     /**
      * Return the current surface output {@link android.graphics.ImageFormat}
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public @ImageFormat.Format int getImageFormat() {
         return mOutputSurface.imageFormat;
     }
diff --git a/core/java/android/hardware/camera2/extension/CharacteristicsMap.java b/core/java/android/hardware/camera2/extension/CharacteristicsMap.java
index 495abc8..e578f6e 100644
--- a/core/java/android/hardware/camera2/extension/CharacteristicsMap.java
+++ b/core/java/android/hardware/camera2/extension/CharacteristicsMap.java
@@ -36,7 +36,6 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public class CharacteristicsMap {
     private final HashMap<String, CameraCharacteristics> mCharMap;
 
@@ -46,7 +45,6 @@
      * @param charsMap       Maps camera ids to respective
      *                       {@link CameraCharacteristics}
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     CharacteristicsMap(@NonNull Map<String, CameraMetadataNative> charsMap) {
         mCharMap = new HashMap<>();
         for (Map.Entry<String, CameraMetadataNative> entry : charsMap.entrySet()) {
@@ -59,7 +57,6 @@
      *
      * @return Set of the camera ids stored in the map
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public Set<String> getCameraIds() {
         return mCharMap.keySet();
@@ -74,7 +71,6 @@
      * @return Valid {@link CameraCharacteristics} instance of null
      *         in case the camera id is not part of the map
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @Nullable
     public CameraCharacteristics get(@NonNull String cameraId) {
         return mCharMap.get(cameraId);
diff --git a/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java b/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java
index 32de1ce..43dfaa5 100644
--- a/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java
+++ b/core/java/android/hardware/camera2/extension/ExtensionConfiguration.java
@@ -43,7 +43,6 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public class ExtensionConfiguration {
     private final int mSessionType;
     private final int mSessionTemplateId;
@@ -65,7 +64,6 @@
      * @param sessionParams     An optional set of camera capture
      *                          session parameter values
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public ExtensionConfiguration(@CameraDevice.SessionOperatingMode int sessionType,
             @CameraDevice.RequestTemplate int sessionTemplateId,
             @NonNull List<ExtensionOutputConfiguration> outputs,
@@ -87,7 +85,6 @@
         mColorSpace = colorSpace;
     }
 
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     CameraSessionConfig getCameraSessionConfig() {
         if (mOutputs.isEmpty()) {
             return null;
diff --git a/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java b/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java
index 8a47430..ff0d8d7b 100644
--- a/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java
+++ b/core/java/android/hardware/camera2/extension/ExtensionOutputConfiguration.java
@@ -35,7 +35,6 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public class ExtensionOutputConfiguration {
     private final List<CameraOutputSurface> mSurfaces;
     private final String mPhysicalCameraId;
@@ -57,7 +56,6 @@
      * @param surfaceGroupId    In case of surface group, this field must
      *                          contain the surface group id
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public ExtensionOutputConfiguration(@NonNull List<CameraOutputSurface> outputs,
             int outputConfigId, @Nullable String physicalCameraId, int surfaceGroupId) {
         mSurfaces = outputs;
diff --git a/core/java/android/hardware/camera2/extension/RequestProcessor.java b/core/java/android/hardware/camera2/extension/RequestProcessor.java
index 0ad27c2..936d57b 100644
--- a/core/java/android/hardware/camera2/extension/RequestProcessor.java
+++ b/core/java/android/hardware/camera2/extension/RequestProcessor.java
@@ -45,19 +45,16 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public final class RequestProcessor {
     private final static String TAG = "RequestProcessor";
     private final IRequestProcessorImpl mRequestProcessor;
     private final long mVendorId;
 
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     RequestProcessor (@NonNull IRequestProcessorImpl requestProcessor, long vendorId) {
         mRequestProcessor = requestProcessor;
         mVendorId = vendorId;
     }
 
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public interface RequestCallback {
         /**
          * This method is called when the camera device has started
@@ -76,7 +73,6 @@
          * @param frameNumber the frame number for this capture
          *
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureStarted(@NonNull Request request, long frameNumber, long timestamp);
 
         /**
@@ -117,7 +113,6 @@
          *                      which includes a subset of the {@link
          *                      TotalCaptureResult} fields.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureProgressed(@NonNull Request request, @NonNull CaptureResult partialResult);
 
         /**
@@ -138,7 +133,6 @@
          *                           parameters and the state of the camera
          *                           system during capture.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureCompleted(@NonNull Request request,
                 @Nullable TotalCaptureResult totalCaptureResult);
 
@@ -163,7 +157,6 @@
          * @param failure The output failure from the capture, including the
          *                failure reason and the frame number.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureFailed(@NonNull Request request, @NonNull CaptureFailure failure);
 
         /**
@@ -182,7 +175,6 @@
          * @param outputStreamId The output stream id that the buffer will not
          *                       be produced for
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureBufferLost(@NonNull Request request, long frameNumber, int outputStreamId);
 
         /**
@@ -203,7 +195,6 @@
          *                    or {@link CaptureFailure#getFrameNumber}) in
          *                    the capture sequence.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureSequenceCompleted(int sequenceId, long frameNumber);
 
         /**
@@ -221,17 +212,14 @@
          * @param sequenceId A sequence ID returned by the RequestProcessor
          *                   capture family of methods
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureSequenceAborted(int sequenceId);
     }
 
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public final static class Request {
         private final List<Integer> mOutputIds;
         private final List<Pair<CaptureRequest.Key, Object>> mParameters;
         private final @CameraDevice.RequestTemplate int mTemplateId;
 
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         public Request(@NonNull List<Integer> outputConfigIds,
                 @NonNull List<Pair<CaptureRequest.Key, Object>> parameters,
                 @CameraDevice.RequestTemplate int templateId) {
@@ -244,7 +232,6 @@
          * Gets the target ids of {@link ExtensionOutputConfiguration} which identifies
          * corresponding Surface to be the targeted for the request.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         @NonNull
         List<Integer> getOutputConfigIds() {
             return mOutputIds;
@@ -253,7 +240,6 @@
         /**
          * Gets all the parameters.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         @NonNull
         public List<Pair<CaptureRequest.Key, Object>> getParameters() {
             return mParameters;
@@ -265,7 +251,6 @@
          *
          * @see CameraDevice.RequestTemplate
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         Integer getTemplateId() {
             return mTemplateId;
         }
@@ -322,7 +307,6 @@
      * @param callback Request callback implementation
      * @return the id of the capture sequence
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public int submit(@NonNull Request request, @NonNull Executor executor,
             @NonNull RequestCallback callback) throws CameraAccessException {
         ArrayList<Request> requests = new ArrayList<>(1);
@@ -355,7 +339,6 @@
      * @param callback Request callback implementation
      * @return the id of the capture sequence
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public int submitBurst(@NonNull List<Request> requests, @NonNull Executor executor,
             @NonNull RequestCallback callback) throws CameraAccessException {
         List<android.hardware.camera2.extension.Request> parcelableRequests =
@@ -386,7 +369,6 @@
      * @param callback Request callback implementation
      * @return the id of the capture sequence
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public int setRepeating(@NonNull Request request, @NonNull Executor executor,
             @NonNull RequestCallback callback) throws CameraAccessException {
         ArrayList<Request> requests = new ArrayList<>(1);
@@ -413,7 +395,6 @@
     /**
      * Abort all ongoing capture requests.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public void abortCaptures() {
         try {
             mRequestProcessor.abortCaptures();
@@ -425,7 +406,6 @@
     /**
      * Stop the current repeating request.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public void stopRepeating() {
         try {
             mRequestProcessor.stopRepeating();
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index eead4ef..cf9f30d 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -76,7 +76,6 @@
  * @hide
  */
 @SystemApi
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public abstract class SessionProcessor {
     private static final String TAG = "SessionProcessor";
     private CameraUsageTracker mCameraUsageTracker;
@@ -84,7 +83,6 @@
     /**
      * Initialize a session process instance
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public SessionProcessor() {}
 
     void setCameraUsageTracker(CameraUsageTracker tracker) {
@@ -97,7 +95,6 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public interface CaptureCallback {
         /**
          * This method is called when the camera device has started
@@ -116,7 +113,6 @@
          *                          first frame in a multi-frame capture,
          *                          in nanoseconds.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureStarted(int captureSequenceId, long timestamp);
 
         /**
@@ -126,7 +122,6 @@
          *
          * @param captureSequenceId id of the current capture sequence
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureProcessStarted(int captureSequenceId);
 
         /**
@@ -139,7 +134,6 @@
          * @param captureSequenceId id of the current capture sequence
          * @param failure           The capture failure reason
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureFailed(int captureSequenceId, @CaptureFailure.FailureReason int failure);
 
         /**
@@ -154,7 +148,6 @@
          *
          * @param captureSequenceId id of the current capture sequence
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureSequenceCompleted(int captureSequenceId);
 
         /**
@@ -162,7 +155,6 @@
          *
          * @param captureSequenceId id of the current capture sequence
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureSequenceAborted(int captureSequenceId);
 
         /**
@@ -192,7 +184,6 @@
          *                  settings and results are always supported and
          *                  applied by the corresponding framework.
          */
-        @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureCompleted(long shutterTimestamp, int requestId,
                 @NonNull Map<CaptureResult.Key, Object> results);
     }
@@ -231,7 +222,6 @@
      * ensure this list will always  produce a valid camera capture
      * session.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public abstract ExtensionConfiguration initSession(@NonNull IBinder token,
             @NonNull String cameraId, @NonNull CharacteristicsMap map,
@@ -247,7 +237,6 @@
      * @param token Binder token that can be used to unlink any previously
      *              linked death notifier callbacks
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract void deInitSession(@NonNull IBinder token);
 
     /**
@@ -268,7 +257,6 @@
      *
      *
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract void onCaptureSessionStart(@NonNull RequestProcessor requestProcessor,
             @NonNull String statsKey);
 
@@ -279,7 +267,6 @@
      * will no longer accept any requests after onCaptureSessionEnd()
      * returns.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract void onCaptureSessionEnd();
 
     /**
@@ -293,7 +280,6 @@
      * @param callback a callback to report the status.
      * @return the id of the capture sequence.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract int startRepeating(@NonNull Executor executor,
             @NonNull CaptureCallback callback);
 
@@ -305,7 +291,6 @@
      * forward calling {@link RequestProcessor#setRepeating} will simply
      * do nothing.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract void stopRepeating();
 
     /**
@@ -326,7 +311,6 @@
      * @param callback a callback to report the status.
      * @return the id of the capture sequence.
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract int startMultiFrameCapture(@NonNull Executor executor,
             @NonNull CaptureCallback callback);
 
@@ -338,7 +322,6 @@
      * the value.
      *@param captureRequest Request that includes all client parameter
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract void setParameters(@NonNull CaptureRequest captureRequest);
 
     /**
@@ -357,7 +340,6 @@
      * @return the id of the capture sequence.
      *
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public abstract int startTrigger(@NonNull CaptureRequest captureRequest,
             @NonNull Executor executor, @NonNull CaptureCallback callback);
 
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 323712d..fc03b51 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -873,17 +873,15 @@
 
         @Override
         public void onCaptureProcessFailed(int captureSequenceId, int captureFailureReason) {
-            if (Flags.concertMode()) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    mClientExecutor.execute(
-                            () -> mClientCallbacks.onCaptureFailed(
-                                     CameraAdvancedExtensionSessionImpl.this, mClientRequest,
-                                    captureFailureReason
-                            ));
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mClientExecutor.execute(
+                        () -> mClientCallbacks.onCaptureFailed(
+                                 CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+                                captureFailureReason
+                        ));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
         }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 0cd1c8c..ef7f3f8 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1797,57 +1797,49 @@
             return false;
         }
 
-        if (Flags.concertMode()) {
-            long[] tsArray = new long[samples.length];
-            float[] intrinsicsArray = new float[samples.length * 5];
-            for (int i = 0; i < samples.length; i++) {
-                tsArray[i] = samples[i].getTimestampNanos();
-                System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5 * i, 5);
+        long[] tsArray = new long[samples.length];
+        float[] intrinsicsArray = new float[samples.length * 5];
+        for (int i = 0; i < samples.length; i++) {
+            tsArray[i] = samples[i].getTimestampNanos();
+            System.arraycopy(samples[i].getLensIntrinsics(), 0, intrinsicsArray, 5 * i, 5);
 
-            }
-            setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray);
-            setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray);
-
-            return true;
-        } else {
-            return false;
         }
+        setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES, intrinsicsArray);
+        setBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS, tsArray);
+
+        return true;
     }
 
     private LensIntrinsicsSample[] getLensIntrinsicSamples() {
-        if (Flags.concertMode()) {
-            long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS);
-            float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES);
+        long[] timestamps = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_TIMESTAMPS);
+        float[] intrinsics = getBase(CaptureResult.STATISTICS_LENS_INTRINSIC_SAMPLES);
 
-            if (timestamps == null) {
-                if (intrinsics != null) {
-                    throw new AssertionError("timestamps is null but intrinsics is not");
-                }
-
-                return null;
+        if (timestamps == null) {
+            if (intrinsics != null) {
+                throw new AssertionError("timestamps is null but intrinsics is not");
             }
 
-            if (intrinsics == null) {
-                throw new AssertionError("timestamps is not null but intrinsics is");
-            } else if ((intrinsics.length % 5) != 0) {
-                throw new AssertionError("intrinsics are not multiple of 5");
-            }
-
-            if ((intrinsics.length / 5) != timestamps.length) {
-                throw new AssertionError(String.format(
-                        "timestamps has %d entries but intrinsics has %d", timestamps.length,
-                        intrinsics.length / 5));
-            }
-
-            LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length];
-            for (int i = 0; i < timestamps.length; i++) {
-                float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5 * i, 5 * i + 5);
-                samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic);
-            }
-            return samples;
-        } else {
             return null;
         }
+
+        if (intrinsics == null) {
+            throw new AssertionError("timestamps is not null but intrinsics is");
+        } else if ((intrinsics.length % 5) != 0) {
+            throw new AssertionError("intrinsics are not multiple of 5");
+        }
+
+        if ((intrinsics.length / 5) != timestamps.length) {
+            throw new AssertionError(String.format(
+                    "timestamps has %d entries but intrinsics has %d", timestamps.length,
+                    intrinsics.length / 5));
+        }
+
+        LensIntrinsicsSample[] samples = new LensIntrinsicsSample[timestamps.length];
+        for (int i = 0; i < timestamps.length; i++) {
+            float[] currentIntrinsic = Arrays.copyOfRange(intrinsics, 5 * i, 5 * i + 5);
+            samples[i] = new LensIntrinsicsSample(timestamps[i], currentIntrinsic);
+        }
+        return samples;
     }
 
     private Capability[] getExtendedSceneModeCapabilities() {
diff --git a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
index 9a4ec5c..9157ef10 100644
--- a/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
+++ b/core/java/android/hardware/camera2/params/LensIntrinsicsSample.java
@@ -31,7 +31,6 @@
  * Immutable class to store an
  * {@link CaptureResult#STATISTICS_LENS_INTRINSICS_SAMPLES lens intrinsics intra-frame sample}.
  */
-@FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public final class LensIntrinsicsSample {
     /**
      * Create a new {@link LensIntrinsicsSample}.
@@ -46,7 +45,6 @@
      *
      * @throws IllegalArgumentException if lensIntrinsics length is different from 5
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public LensIntrinsicsSample(final long timestampNs, @NonNull final float[] lensIntrinsics) {
         mTimestampNs = timestampNs;
         Preconditions.checkArgument(lensIntrinsics.length == 5);
@@ -61,7 +59,6 @@
      *
      * @return a long value (guaranteed to be finite)
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public long getTimestampNanos() {
         return mTimestampNs;
     }
@@ -72,7 +69,6 @@
      * @return a floating point value (guaranteed to be finite)
      * @see CaptureResult#LENS_INTRINSIC_CALIBRATION
      */
-    @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @NonNull
     public float[] getLensIntrinsics() {
         return mLensIntrinsics;
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index a2d24f6..73b5d94 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -432,6 +432,11 @@
     public abstract IntArray getDisplayGroupIds();
 
     /**
+     * Get all available display ids.
+     */
+    public abstract IntArray getDisplayIds();
+
+    /**
      * Called upon presentation started/ended on the display.
      * @param displayId the id of the display where presentation started.
      * @param isShown whether presentation is shown.
diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.java b/core/java/android/hardware/face/FaceSensorConfigurations.java
index 1247168..51c5f4c 100644
--- a/core/java/android/hardware/face/FaceSensorConfigurations.java
+++ b/core/java/android/hardware/face/FaceSensorConfigurations.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.face.virtualhal.IVirtualHal;
 import android.os.Binder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -160,6 +161,41 @@
         dest.writeByte((byte) (mResetLockoutRequiresChallenge ? 1 : 0));
         dest.writeMap(mSensorPropsMap);
     }
+    /**
+     * Remap fqName of VHAL because the `virtual` instance is registered
+     * with IVirtulalHal now (IFace previously)
+     * @param fqName fqName to be translated
+     * @return real fqName
+     */
+    public static String remapFqName(String fqName) {
+        if (!fqName.contains(IFace.DESCRIPTOR + "/virtual")) {
+            return fqName;  //no remap needed for real hardware HAL
+        } else {
+            //new Vhal instance name
+            return fqName.replace("IFace", "virtualhal.IVirtualHal");
+        }
+    }
+    /**
+     * @param fqName aidl interface instance name
+     * @return aidl interface
+     */
+    public static IFace getIFace(String fqName) {
+        if (fqName.contains("virtual")) {
+            String fqNameMapped = remapFqName(fqName);
+            Slog.i(TAG, "getIFace fqName is mapped: " + fqName + "->" + fqNameMapped);
+            try {
+                IVirtualHal vhal = IVirtualHal.Stub.asInterface(
+                        Binder.allowBlocking(ServiceManager.waitForService(fqNameMapped)));
+                return vhal.getFaceHal();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception in vhal.getFaceHal() call" + fqNameMapped);
+            }
+        }
+
+        return IFace.Stub.asInterface(
+                Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+    }
+
 
     /**
      * Returns face sensor props for the HAL {@param instance}.
@@ -173,14 +209,13 @@
             return props;
         }
 
-        final String fqName = IFace.DESCRIPTOR + "/" + instance;
-        IFace face = IFace.Stub.asInterface(Binder.allowBlocking(
-                ServiceManager.waitForDeclaredService(fqName)));
         try {
-            if (face != null) {
-                props = face.getSensorProps();
+            final String fqName = IFace.DESCRIPTOR + "/" + instance;
+            final IFace fp = getIFace(fqName);
+            if (fp != null) {
+                props = fp.getSensorProps();
             } else {
-                Slog.e(TAG, "Unable to get declared service: " + fqName);
+                Log.d(TAG, "IFace null for instance " + instance);
             }
         } catch (RemoteException e) {
             Log.d(TAG, "Unable to get sensor properties!");
diff --git a/core/java/android/hardware/fingerprint/FingerprintCallback.java b/core/java/android/hardware/fingerprint/FingerprintCallback.java
index e4fbe6e..24e9f9d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintCallback.java
+++ b/core/java/android/hardware/fingerprint/FingerprintCallback.java
@@ -189,7 +189,7 @@
             mEnrollmentCallback.onAcquired(acquireInfo == FINGERPRINT_ACQUIRED_GOOD);
         }
         final String msg = getAcquiredString(context, acquireInfo, vendorCode);
-        if (msg == null) {
+        if (msg == null || msg.isEmpty()) {
             return;
         }
         // emulate HAL 2.1 behavior and send real acquiredInfo
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 0cabc4c..5ee61bc 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -22,8 +22,6 @@
 import android.view.Display;
 import android.view.KeyCharacterMap;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AnnotationValidations;
 import com.android.internal.util.FrameworkStatsLog;
 
 import java.lang.annotation.Retention;
@@ -50,49 +48,57 @@
     public static final int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL = 8;
     public static final int KEY_GESTURE_TYPE_TOGGLE_TASKBAR = 9;
     public static final int KEY_GESTURE_TYPE_TAKE_SCREENSHOT = 10;
-    public static final int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER = 11;
-    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_UP = 12;
-    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN = 13;
-    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP = 14;
-    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN = 15;
-    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE = 16;
-    public static final int KEY_GESTURE_TYPE_VOLUME_UP = 17;
-    public static final int KEY_GESTURE_TYPE_VOLUME_DOWN = 18;
-    public static final int KEY_GESTURE_TYPE_VOLUME_MUTE = 19;
-    public static final int KEY_GESTURE_TYPE_ALL_APPS = 20;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_SEARCH = 21;
-    public static final int KEY_GESTURE_TYPE_LANGUAGE_SWITCH = 22;
-    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS = 23;
-    public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK = 24;
-    public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE = 25;
-    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT = 26;
-    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT = 27;
-    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT = 28;
-    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT = 29;
-    public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT = 30;
-    public static final int KEY_GESTURE_TYPE_LOCK_SCREEN = 31;
-    public static final int KEY_GESTURE_TYPE_OPEN_NOTES = 32;
-    public static final int KEY_GESTURE_TYPE_TOGGLE_POWER = 33;
-    public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION = 34;
-    public static final int KEY_GESTURE_TYPE_SLEEP = 35;
-    public static final int KEY_GESTURE_TYPE_WAKEUP = 36;
-    public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 37;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 38;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 39;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 40;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR = 41;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR = 42;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC = 43;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS = 44;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING = 45;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY = 46;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 47;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 48;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 49;
-    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 50;
-    public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 51;
-    public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 52;
-    public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 53;
+    public static final int KEY_GESTURE_TYPE_SCREENSHOT_CHORD = 11;
+    public static final int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER = 12;
+    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_UP = 13;
+    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN = 14;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP = 15;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN = 16;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE = 17;
+    public static final int KEY_GESTURE_TYPE_VOLUME_UP = 18;
+    public static final int KEY_GESTURE_TYPE_VOLUME_DOWN = 19;
+    public static final int KEY_GESTURE_TYPE_VOLUME_MUTE = 20;
+    public static final int KEY_GESTURE_TYPE_ALL_APPS = 21;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_SEARCH = 22;
+    public static final int KEY_GESTURE_TYPE_LANGUAGE_SWITCH = 23;
+    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS = 24;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK = 25;
+    public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE = 26;
+    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT = 27;
+    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT = 28;
+    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT = 29;
+    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT = 30;
+    public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT = 31;
+    public static final int KEY_GESTURE_TYPE_LOCK_SCREEN = 32;
+    public static final int KEY_GESTURE_TYPE_OPEN_NOTES = 33;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_POWER = 34;
+    public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION = 35;
+    public static final int KEY_GESTURE_TYPE_SLEEP = 36;
+    public static final int KEY_GESTURE_TYPE_WAKEUP = 37;
+    public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 38;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 39;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 40;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 41;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR = 42;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR = 43;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC = 44;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS = 45;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING = 46;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY = 47;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 48;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 49;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 50;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 51;
+    public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 52;
+    public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 53;
+    public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 54;
+    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD = 55;
+    public static final int KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD = 56;
+    public static final int KEY_GESTURE_TYPE_GLOBAL_ACTIONS = 57;
+    public static final int KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD = 58;
+    public static final int KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT = 59;
+    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT = 60;
+    public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61;
 
     public static final int FLAG_CANCELLED = 1;
 
@@ -118,6 +124,7 @@
             KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
             KEY_GESTURE_TYPE_TOGGLE_TASKBAR,
             KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+            KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
             KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
             KEY_GESTURE_TYPE_BRIGHTNESS_UP,
             KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
@@ -160,7 +167,15 @@
             KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
             KEY_GESTURE_TYPE_DESKTOP_MODE,
             KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
-            KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
+            KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+            KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
+            KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD,
+            KEY_GESTURE_TYPE_GLOBAL_ACTIONS,
+            KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
+            KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
+            KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
+            KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
+
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KeyGestureType {
@@ -171,6 +186,14 @@
     }
 
     /**
+     * Tests whether this keyboard shortcut event has the given modifiers (i.e. all of the given
+     * modifiers were pressed when this shortcut was triggered).
+     */
+    public boolean hasModifiers(int modifiers) {
+        return (getModifierState() & modifiers) == modifiers;
+    }
+
+    /**
      * Key gesture event builder used to create a KeyGestureEvent for tests in Java.
      *
      * @hide
@@ -354,6 +377,7 @@
             case KEY_GESTURE_TYPE_TOGGLE_TASKBAR:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR;
             case KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+            case KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT;
             case KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
                 return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER;
@@ -466,6 +490,8 @@
                 return "KEY_GESTURE_TYPE_TOGGLE_TASKBAR";
             case KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
                 return "KEY_GESTURE_TYPE_TAKE_SCREENSHOT";
+            case KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
+                return "KEY_GESTURE_TYPE_SCREENSHOT_CHORD";
             case KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
                 return "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER";
             case KEY_GESTURE_TYPE_BRIGHTNESS_UP:
@@ -552,6 +578,20 @@
                 return "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION";
             case KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
                 return "KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER";
+            case KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
+                return "KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD";
+            case KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
+                return "KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD";
+            case KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
+                return "KEY_GESTURE_TYPE_GLOBAL_ACTIONS";
+            case KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
+                return "KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD";
+            case KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
+                return "KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT";
+            case KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
+                return "KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT";
+            case KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
+                return "KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS";
             default:
                 return Integer.toHexString(value);
         }
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 983bbc3..75683f6 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -138,3 +138,10 @@
   description: "Controls whether external mouse vertical scrolling can be reversed"
   bug: "352598211"
 }
+
+flag {
+  name: "mouse_swap_primary_button"
+  namespace: "input"
+  description: "Controls whether the connected mice's primary buttons, left and right, can be swapped."
+  bug: "352598211"
+}
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 5c07fa4..22ae676 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -155,16 +155,16 @@
     public static RecognitionConfig api2aidlRecognitionConfig(
             SoundTrigger.RecognitionConfig apiConfig) {
         RecognitionConfig aidlConfig = new RecognitionConfig();
-        aidlConfig.captureRequested = apiConfig.captureRequested;
-        // apiConfig.allowMultipleTriggers is ignored by the lower layers.
+        aidlConfig.captureRequested = apiConfig.isCaptureRequested();
+        // apiConfig.isAllowMultipleTriggers() is ignored by the lower layers.
         aidlConfig.phraseRecognitionExtras =
-                new PhraseRecognitionExtra[apiConfig.keyphrases.length];
-        for (int i = 0; i < apiConfig.keyphrases.length; ++i) {
+                new PhraseRecognitionExtra[apiConfig.getKeyphrases().size()];
+        for (int i = 0; i < apiConfig.getKeyphrases().size(); ++i) {
             aidlConfig.phraseRecognitionExtras[i] = api2aidlPhraseRecognitionExtra(
-                    apiConfig.keyphrases[i]);
+                    apiConfig.getKeyphrases().get(i));
         }
-        aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length);
-        aidlConfig.audioCapabilities = api2aidlAudioCapabilities(apiConfig.audioCapabilities);
+        aidlConfig.data = Arrays.copyOf(apiConfig.getData(), apiConfig.getData().length);
+        aidlConfig.audioCapabilities = api2aidlAudioCapabilities(apiConfig.getAudioCapabilities());
         return aidlConfig;
     }
 
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 9f3e3ad..05e91e4 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -63,6 +63,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.UUID;
@@ -1513,50 +1514,60 @@
      *  A RecognitionConfig is provided to
      *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
      *  recognition request.
-     *
-     *  @hide
      */
-    @TestApi
+    @FlaggedApi(android.media.soundtrigger.Flags.FLAG_MANAGER_API)
     public static final class RecognitionConfig implements Parcelable {
-        /** True if the DSP should capture the trigger sound and make it available for further
-         * capture. */
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public final boolean captureRequested;
-        /**
-         * True if the service should restart listening after the DSP triggers.
-         * Note: This config flag is currently used at the service layer rather than by the DSP.
-         */
-        public final boolean allowMultipleTriggers;
-        /** List of all keyphrases in the sound model for which recognition should be performed with
-         * options for each keyphrase. */
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        @NonNull
-        @SuppressLint("ArrayReturn")
-        public final KeyphraseRecognitionExtra keyphrases[];
-        /** Opaque data for use by system applications who know about voice engine internals,
-         * typically during enrollment. */
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        @NonNull
-        public final byte[] data;
-
-        /**
-         * Bit field encoding of the AudioCapabilities
-         * supported by the firmware.
-         */
+        private final boolean mCaptureRequested;
+        private final boolean mAllowMultipleTriggers;
+        private final KeyphraseRecognitionExtra mKeyphrases[];
+        private final byte[] mData;
         @ModuleProperties.AudioCapabilities
-        public final int audioCapabilities;
+        private final int mAudioCapabilities;
 
+        /**
+         * Constructor for {@link RecognitionConfig} with {@code audioCapabilities} describes a
+         * config that can be used by
+         * {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)}
+         *
+         * @deprecated should use builder-based constructor instead.
+         *             TODO(b/368042125): remove this method.
+         * @param captureRequested Whether the DSP should capture the trigger sound.
+         * @param allowMultipleTriggers Whether the service should restart listening after the DSP
+         *                              triggers.
+         * @param keyphrases List of keyphrases in the sound model.
+         * @param data Opaque data for use by system applications who know about voice engine
+         *             internals, typically during enrollment.
+         * @param audioCapabilities Bit field encoding of the AudioCapabilities.
+         *
+         * @hide
+         */
+        @Deprecated
+        @SuppressWarnings("Todo")
+        @TestApi
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
                 @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases,
                 @Nullable byte[] data, int audioCapabilities) {
-            this.captureRequested = captureRequested;
-            this.allowMultipleTriggers = allowMultipleTriggers;
-            this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
-            this.data = data != null ? data : new byte[0];
-            this.audioCapabilities = audioCapabilities;
+            this.mCaptureRequested = captureRequested;
+            this.mAllowMultipleTriggers = allowMultipleTriggers;
+            this.mKeyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
+            this.mData = data != null ? data : new byte[0];
+            this.mAudioCapabilities = audioCapabilities;
         }
 
+        /**
+         * Constructor for {@link RecognitionConfig} without audioCapabilities. The
+         * audioCapabilities is set to 0.
+         *
+         * @param captureRequested Whether the DSP should capture the trigger sound.
+         * @param allowMultipleTriggers Whether the service should restart listening after the DSP
+         *                              triggers.
+         * @param keyphrases List of keyphrases in the sound model.
+         * @param data Opaque data for use by system applications.
+         *
+         * @hide
+         */
         @UnsupportedAppUsage
+        @TestApi
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
                 @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases,
                 @Nullable byte[] data) {
@@ -1574,9 +1585,52 @@
             }
         };
 
+        /**
+         * Returns whether the DSP should capture the trigger sound and make it available for
+         * further capture.
+         */
+        public boolean isCaptureRequested() {
+            return mCaptureRequested;
+        }
+
+        /**
+         * Returns whether the service should restart listening after the DSP triggers.
+         *
+         * <p><b>Note:</b> This config flag is currently used at the service layer rather than by
+         * the DSP.
+         */
+        public boolean isAllowMultipleTriggers() {
+            return mAllowMultipleTriggers;
+        }
+
+        /**
+         * Gets all keyphrases in the sound model for which recognition should be performed with
+         * options for each keyphrase.
+         */
+        @NonNull
+        public List<KeyphraseRecognitionExtra> getKeyphrases() {
+            return Arrays.asList(mKeyphrases);
+        }
+
+        /**
+         * Opaque data.
+         *
+         * <p>For use by system applications who knows about voice engine internals, typically
+         * during enrollment.
+         */
+        @NonNull
+        public byte[] getData() {
+            return mData;
+        }
+
+        /** Bit field encoding of the AudioCapabilities supported by the firmware. */
+        public int getAudioCapabilities() {
+            return mAudioCapabilities;
+        }
+
         private static RecognitionConfig fromParcel(Parcel in) {
-            boolean captureRequested = in.readByte() == 1;
-            boolean allowMultipleTriggers = in.readByte() == 1;
+            boolean captureRequested = in.readBoolean();
+            boolean allowMultipleTriggers = in.readBoolean();
             KeyphraseRecognitionExtra[] keyphrases =
                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
             byte[] data = in.readBlob();
@@ -1587,11 +1641,11 @@
 
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
-            dest.writeByte((byte) (captureRequested ? 1 : 0));
-            dest.writeByte((byte) (allowMultipleTriggers ? 1 : 0));
-            dest.writeTypedArray(keyphrases, flags);
-            dest.writeBlob(data);
-            dest.writeInt(audioCapabilities);
+            dest.writeBoolean(mCaptureRequested);
+            dest.writeBoolean(mAllowMultipleTriggers);
+            dest.writeTypedArray(mKeyphrases, flags);
+            dest.writeBlob(mData);
+            dest.writeInt(mAudioCapabilities);
         }
 
         @Override
@@ -1601,10 +1655,10 @@
 
         @Override
         public String toString() {
-            return "RecognitionConfig [captureRequested=" + captureRequested
-                    + ", allowMultipleTriggers=" + allowMultipleTriggers + ", keyphrases="
-                    + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data)
-                    + ", audioCapabilities=" + Integer.toHexString(audioCapabilities) + "]";
+            return "RecognitionConfig [captureRequested=" + mCaptureRequested
+                    + ", allowMultipleTriggers=" + mAllowMultipleTriggers + ", keyphrases="
+                    + Arrays.toString(mKeyphrases) + ", data=" + Arrays.toString(mData)
+                    + ", audioCapabilities=" + Integer.toHexString(mAudioCapabilities) + "]";
         }
 
         @Override
@@ -1616,19 +1670,19 @@
             if (!(obj instanceof RecognitionConfig))
                 return false;
             RecognitionConfig other = (RecognitionConfig) obj;
-            if (captureRequested != other.captureRequested) {
+            if (mCaptureRequested != other.mCaptureRequested) {
                 return false;
             }
-            if (allowMultipleTriggers != other.allowMultipleTriggers) {
+            if (mAllowMultipleTriggers != other.mAllowMultipleTriggers) {
                 return false;
             }
-            if (!Arrays.equals(keyphrases, other.keyphrases)) {
+            if (!Arrays.equals(mKeyphrases, other.mKeyphrases)) {
                 return false;
             }
-            if (!Arrays.equals(data, other.data)) {
+            if (!Arrays.equals(mData, other.mData)) {
                 return false;
             }
-            if (audioCapabilities != other.audioCapabilities) {
+            if (mAudioCapabilities != other.mAudioCapabilities) {
                 return false;
             }
             return true;
@@ -1638,13 +1692,96 @@
         public final int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + (captureRequested ? 1 : 0);
-            result = prime * result + (allowMultipleTriggers ? 1 : 0);
-            result = prime * result + Arrays.hashCode(keyphrases);
-            result = prime * result + Arrays.hashCode(data);
-            result = prime * result + audioCapabilities;
+            result = prime * result + (mCaptureRequested ? 1 : 0);
+            result = prime * result + (mAllowMultipleTriggers ? 1 : 0);
+            result = prime * result + Arrays.hashCode(mKeyphrases);
+            result = prime * result + Arrays.hashCode(mData);
+            result = prime * result + mAudioCapabilities;
             return result;
         }
+
+        /**
+         * Builder class for {@link RecognitionConfig} objects.
+         */
+        public static final class Builder {
+            private boolean mCaptureRequested;
+            private boolean mAllowMultipleTriggers;
+            @Nullable private KeyphraseRecognitionExtra[] mKeyphrases;
+            @Nullable private byte[] mData;
+            private int mAudioCapabilities;
+
+            /**
+             * Constructs a new Builder with the default values.
+             */
+            public Builder() {
+            }
+
+            /**
+             * Sets capture requested state.
+             * @param captureRequested The new requested state.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder setCaptureRequested(boolean captureRequested) {
+                mCaptureRequested = captureRequested;
+                return this;
+            }
+
+            /**
+             * Sets allow multiple triggers state.
+             * @param allowMultipleTriggers The new allow multiple triggers state.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder setAllowMultipleTriggers(boolean allowMultipleTriggers) {
+                mAllowMultipleTriggers = allowMultipleTriggers;
+                return this;
+            }
+
+            /**
+             * Sets the keyphrases field.
+             * @param keyphrases The new keyphrases.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder setKeyphrases(
+                    @NonNull Collection<KeyphraseRecognitionExtra> keyphrases) {
+                mKeyphrases = keyphrases.toArray(new KeyphraseRecognitionExtra[keyphrases.size()]);
+                return this;
+            }
+
+            /**
+             * Sets the data field.
+             * @param data The new data.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder setData(@Nullable byte[] data) {
+                mData = data;
+                return this;
+            }
+
+            /**
+             * Sets the audio capabilities field.
+             * @param audioCapabilities The new audio capabilities.
+             * @return the same Builder instance.
+             */
+            public @NonNull Builder setAudioCapabilities(int audioCapabilities) {
+                mAudioCapabilities = audioCapabilities;
+                return this;
+            }
+
+            /**
+             * Combines all of the parameters that have been set and return a new
+             * {@link RecognitionConfig} object.
+             * @return a new {@link RecognitionConfig} object
+             */
+            public @NonNull RecognitionConfig build() {
+                RecognitionConfig config = new RecognitionConfig(
+                        /* captureRequested= */ mCaptureRequested,
+                        /* allowMultipleTriggers= */ mAllowMultipleTriggers,
+                        /* keyphrases= */ mKeyphrases,
+                        /* data= */ mData,
+                        /* audioCapabilities= */ mAudioCapabilities);
+                return config;
+            }
+        };
     }
 
     /**
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 91cdf8d..1c9be6f 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -76,11 +76,15 @@
  * PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} before querying the service. If the feature is
  * absent, {@link Context#getSystemService} may return null.
  */
-@SystemService(Context.VCN_MANAGEMENT_SERVICE)
+@SystemService(VcnManager.VCN_MANAGEMENT_SERVICE_STRING)
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
 public class VcnManager {
     @NonNull private static final String TAG = VcnManager.class.getSimpleName();
 
+    // TODO: b/366598445: Expose and use Context.VCN_MANAGEMENT_SERVICE
+    /** @hide */
+    public static final String VCN_MANAGEMENT_SERVICE_STRING = "vcn_management";
+
     /**
      * Key for WiFi entry RSSI thresholds
      *
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 1fef602..a698b9d 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -788,6 +788,12 @@
     /** Parses an XML representation of BatteryUsageStats */
     public static BatteryUsageStats createFromXml(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException {
+        return createBuilderFromXml(parser).build();
+    }
+
+    /** Parses an XML representation of BatteryUsageStats */
+    public static BatteryUsageStats.Builder createBuilderFromXml(TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
         Builder builder = null;
         int eventType = parser.getEventType();
         while (eventType != XmlPullParser.END_DOCUMENT) {
@@ -862,7 +868,7 @@
             eventType = parser.next();
         }
 
-        return builder.build();
+        return builder;
     }
 
     @Override
@@ -978,10 +984,21 @@
          */
         @NonNull
         public BatteryUsageStats build() {
+            if (mBatteryConsumersCursorWindow == null) {
+                throw new IllegalStateException("Builder has been discarded");
+            }
             return new BatteryUsageStats(this);
         }
 
         /**
+         * Close this builder without actually calling ".build()". Do not attempt
+         * to continue using the builder after this call.
+         */
+        public void discard() {
+            mBatteryConsumersCursorWindow.close();
+        }
+
+        /**
          * Sets the battery capacity in milli-amp-hours.
          */
         public Builder setBatteryCapacity(double batteryCapacityMah) {
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index a12606b..b533225 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -77,6 +77,8 @@
 
     public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE = 0x0040;
 
+    public static final int FLAG_BATTERY_USAGE_STATS_ACCUMULATED = 0x0080;
+
     private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000;
 
     private final int mFlags;
@@ -328,6 +330,15 @@
         }
 
         /**
+         * Requests the full continuously accumulated battery usage stats: across reboots
+         * and most battery stats resets.
+         */
+        public Builder accumulated() {
+            mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED;
+            return this;
+        }
+
+        /**
          * Requests to aggregate stored snapshots between the two supplied timestamps
          * @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis()
          * @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis()
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 97e9f34..ed75491 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1111,6 +1111,21 @@
     }
 
     /**
+     * Called whenever the stub implementation throws an exception which isn't propagated to the
+     * remote caller by the binder. If this method isn't overridden, this exception is swallowed,
+     * and some default return values are propagated to the caller.
+     *
+     * <br> <b> This should not throw. </b> Doing so would defeat the purpose of this handler, and
+     * suppress the exception it is handling.
+     *
+     * @param code The transaction code being handled
+     * @param e The exception which was thrown.
+     * @hide
+     */
+    protected void onUnhandledException(int code, int flags, Exception e) {
+    }
+
+    /**
      * @param in The raw file descriptor that an input data stream can be read from.
      * @param out The raw file descriptor that normal command messages should be written to.
      * @param err The raw file descriptor that command error messages should be written to.
@@ -1408,10 +1423,15 @@
                 } else {
                     Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
                 }
+                onUnhandledException(code, flags, e);
             } else {
                 // Clear the parcel before writing the exception.
                 reply.setDataSize(0);
                 reply.setDataPosition(0);
+                // The writeException below won't do anything useful if this is the case.
+                if (Parcel.getExceptionCode(e) == 0) {
+                    onUnhandledException(code, flags, e);
+                }
                 reply.writeException(e);
             }
             res = true;
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index dbb6f92..5f62b8b 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -46,6 +46,7 @@
 
     void userActivity(int displayId, long time, int event, int flags);
     void wakeUp(long time, int reason, String details, String opPackageName);
+    void wakeUpWithDisplayId(long time, int reason, String details, String opPackageName, int displayId);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void goToSleep(long time, int reason, int flags);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 00ba3bf..18f9b2b 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -139,7 +139,7 @@
     boolean isUserForeground(int userId);
     boolean isUserVisible(int userId);
     int[] getVisibleUsers();
-    int getMainDisplayIdAssignedToUser();
+    int getMainDisplayIdAssignedToUser(int userId);
     boolean isForegroundUserAdmin();
     boolean isUserNameSet(int userId);
     boolean hasRestrictedProfiles(int userId);
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 0776cf4..e2a72dd 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -48,6 +48,20 @@
  * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
  * but doesn't hold a lock across data fetches on query misses.
  *
+ * Clients should be aware of the following commonly-seen issues:
+ * <ul>
+ *
+ * <li>Client calls will not go through the cache before the first invalidation signal is
+ * received. Therefore, servers should signal an invalidation as soon as they have data to offer to
+ * clients.
+ *
+ * <li>Cache invalidation is restricted to well-known processes, which means that test code cannot
+ * invalidate a cache.  {@link #disableForTestMode()} and {@link #testPropertyName} must be used in
+ * test processes that attempt cache invalidation.  See
+ * {@link PropertyInvalidatedCacheTest#testBasicCache()} for an example.
+ *
+ * </ul>
+ *
  * The intended use case is caching frequently-read, seldom-changed information normally retrieved
  * across interprocess communication. Imagine that you've written a user birthday information
  * daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface over
@@ -136,20 +150,20 @@
  * string is permitted.  The third parameters is the name of the API being cached; this, too, can
  * any value.  The fourth is the name of the cache.  The cache is usually named after th API.
  * Some things you must know about the three strings:
- * <list>
- * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
+ * <ul>
+ * <li> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
  * Usually, the SELinux rules permit a process to write a system property (and therefore
  * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}.  This means that
  * although the cache can be constructed with any module string, whatever string is chosen must be
  * consistent with the SELinux configuration.
- * <ul> The API name can be any string of alphanumeric characters.  All caches with the same API
+ * <li> The API name can be any string of alphanumeric characters.  All caches with the same API
  * are invalidated at the same time.  If a server supports several caches and all are invalidated
  * in common, then it is most efficient to assign the same API string to every cache.
- * <ul> The cache name can be any string.  In debug output, the name is used to distiguish between
+ * <li> The cache name can be any string.  In debug output, the name is used to distiguish between
  * caches with the same API name.  The cache name is also used when disabling caches in the
  * current process.  So, invalidation is based on the module+api but disabling (which is generally
  * a once-per-process operation) is based on the cache name.
- * </list>
+ * </ul>
  *
  * User birthdays do occasionally change, so we have to modify the server to invalidate this
  * cache when necessary. That invalidation code looks like this:
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index e4c12b6..b9bae5b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1567,27 +1567,9 @@
     }
 
     /**
-     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
-     * to turn on.
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
      *
-     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
-     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
-     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
-     * on then nothing will happen.
-     *
-     * <p>
-     * This is what happens when the power key is pressed to turn on the screen.
-     * </p><p>
-     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
-     * </p>
-     *
-     * @param time The time when the request to wake up was issued, in the
-     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
-     * order the wake up request with other power management functions.  It should be set
-     * to the timestamp of the input event that caused the request to wake up.
-     *
-     * @see #userActivity
-     * @see #goToSleep
+     * @see #wakeUp(long, int, String, int)
      *
      * @deprecated Use {@link #wakeUp(long, int, String)} instead.
      * @removed Requires signature permission.
@@ -1598,30 +1580,9 @@
     }
 
     /**
-     * Forces the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group}
-     * to turn on.
+     * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
      *
-     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is
-     * turned off it will be turned on. Additionally, if the device is asleep it will be awoken. If
-     * the {@link android.view.Display#DEFAULT_DISPLAY_GROUP default display group} is already
-     * on then nothing will happen.
-     *
-     * <p>
-     * This is what happens when the power key is pressed to turn on the screen.
-     * </p><p>
-     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
-     * </p>
-     *
-     * @param time The time when the request to wake up was issued, in the
-     * {@link SystemClock#uptimeMillis()} time base.  This timestamp is used to correctly
-     * order the wake up request with other power management functions.  It should be set
-     * to the timestamp of the input event that caused the request to wake up.
-     *
-     * @param details A free form string to explain the specific details behind the wake up for
-     *                debugging purposes.
-     *
-     * @see #userActivity
-     * @see #goToSleep
+     * @see #wakeUp(long, int, String, int)
      *
      * @deprecated Use {@link #wakeUp(long, int, String)} instead.
      * @hide
@@ -1635,9 +1596,23 @@
     /**
      * Forces the {@link android.view.Display#DEFAULT_DISPLAY default display} to turn on.
      *
-     * <p>If the {@link android.view.Display#DEFAULT_DISPLAY default display} is turned off it will
-     * be turned on. Additionally, if the device is asleep it will be awoken. If the {@link
-     * android.view.Display#DEFAULT_DISPLAY default display} is already on then nothing will happen.
+     * @see #wakeUp(long, int, String, int)
+     * @hide
+     */
+    public void wakeUp(long time, @WakeReason int reason, String details) {
+        try {
+            mService.wakeUp(time, reason, details, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Forces the display with the supplied displayId to turn on.
+     *
+     * <p>If the corresponding display is turned off, it will be turned on. Additionally, if the
+     * device is asleep it will be awoken. If the corresponding display is already on then nothing
+     * will happen. If the corresponding display does not exist, then nothing will happen.
      *
      * <p>If the device is an Android TV playback device, it will attempt to turn on the
      * HDMI-connected TV and become the current active source via the HDMI-CEC One Touch Play
@@ -1658,14 +1633,16 @@
      *
      * @param details A free form string to explain the specific details behind the wake up for
      *                debugging purposes.
+     * @param displayId The displayId of the display to be woken up.
      *
      * @see #userActivity
      * @see #goToSleep
      * @hide
      */
-    public void wakeUp(long time, @WakeReason int reason, String details) {
+    public void wakeUp(long time, @WakeReason int reason, String details, int displayId) {
         try {
-            mService.wakeUp(time, reason, details, mContext.getOpPackageName());
+            mService.wakeUpWithDisplayId(time, reason, details, mContext.getOpPackageName(),
+                    displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 4c9a02c..dfc591b 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -28,6 +28,8 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
+import com.android.internal.os.ApplicationSharedMemory;
+
 import dalvik.annotation.optimization.CriticalNative;
 
 import java.time.Clock;
@@ -323,74 +325,74 @@
     }
 
     /**
-     * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
-     * using a remote network source outside the device.
-     * <p>
-     * While the time returned by {@link System#currentTimeMillis()} can be
-     * adjusted by the user, the time returned by this method cannot be adjusted
-     * by the user.
-     * <p>
-     * This performs no blocking network operations and returns values based on
-     * a recent successful synchronization event; it will either return a valid
-     * time or throw.
-     * <p>
-     * Note that synchronization may occur using an insecure network protocol,
-     * so the returned time should not be used for security purposes.
-     * The device may resynchronize with the same or different network source
-     * at any time. Due to network delays, variations between servers, or local
-     * (client side) clock drift, the accuracy of the returned times cannot be
-     * guaranteed. In extreme cases, consecutive calls to {@link
-     * #currentNetworkTimeMillis(ITimeDetectorService)} could return times that
-     * are out of order.
+     * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized using a remote
+     * network source outside the device.
+     *
+     * <p>While the time returned by {@link System#currentTimeMillis()} can be adjusted by the user,
+     * the time returned by this method cannot be adjusted by the user.
+     *
+     * <p>This performs no blocking network operations and returns values based on a recent
+     * successful synchronization event; it will either return a valid time or throw.
+     *
+     * <p>Note that synchronization may occur using an insecure network protocol, so the returned
+     * time should not be used for security purposes. The device may resynchronize with the same or
+     * different network source at any time. Due to network delays, variations between servers, or
+     * local (client side) clock drift, the accuracy of the returned times cannot be guaranteed. In
+     * extreme cases, consecutive calls to {@link #currentNetworkTimeMillis()} could return times
+     * that are out of order.
      *
      * @throws DateTimeException when no network time can be provided.
      * @hide
      */
     public static long currentNetworkTimeMillis() {
-        ITimeDetectorService timeDetectorService = getITimeDetectorService();
-        if (timeDetectorService == null) {
-            throw new RuntimeException(new DeadSystemException());
-        }
+        if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
+                && Flags.networkTimeUsesSharedMemory()) {
+            final long latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis =
+                    ApplicationSharedMemory.getInstance()
+                            .getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
+            return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis + elapsedRealtime();
+        } else {
+            ITimeDetectorService timeDetectorService = getITimeDetectorService();
+            if (timeDetectorService == null) {
+                throw new RuntimeException(new DeadSystemException());
+            }
 
-        UnixEpochTime time;
-        try {
-            time = timeDetectorService.latestNetworkTime();
-        } catch (ParcelableException e) {
-            e.maybeRethrow(DateTimeException.class);
-            throw new RuntimeException(e);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        if (time == null) {
-            // This is not expected.
-            throw new DateTimeException("Network based time is not available.");
-        }
+            UnixEpochTime time;
+            try {
+                time = timeDetectorService.latestNetworkTime();
+            } catch (ParcelableException e) {
+                e.maybeRethrow(DateTimeException.class);
+                throw new RuntimeException(e);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            if (time == null) {
+                // This is not expected.
+                throw new DateTimeException("Network based time is not available.");
+            }
 
-        long currentMillis = elapsedRealtime();
-        long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
-        return time.getUnixEpochTimeMillis() + deltaMs;
+            long currentMillis = elapsedRealtime();
+            long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
+            return time.getUnixEpochTimeMillis() + deltaMs;
+        }
     }
 
-   /**
-     * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC,
-     * synchronized using a remote network source outside the device.
-     * <p>
-     * While the time returned by {@link System#currentTimeMillis()} can be
-     * adjusted by the user, the time returned by this method cannot be adjusted
-     * by the user.
-     * <p>
-     * This performs no blocking network operations and returns values based on
-     * a recent successful synchronization event; it will either return a valid
-     * time or throw.
-     * <p>
-     * Note that synchronization may occur using an insecure network protocol,
-     * so the returned time should not be used for security purposes.
-     * The device may resynchronize with the same or different network source
-     * at any time. Due to network delays, variations between servers, or local
-     * (client side) clock drift, the accuracy of the returned times cannot be
-     * guaranteed. In extreme cases, consecutive calls to {@link
-     * Clock#millis()} on the returned {@link Clock} could return times that are
-     * out of order.
+    /**
+     * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC, synchronized using a
+     * remote network source outside the device.
+     *
+     * <p>While the time returned by {@link System#currentTimeMillis()} can be adjusted by the user,
+     * the time returned by this method cannot be adjusted by the user.
+     *
+     * <p>This performs no blocking network operations and returns values based on a recent
+     * successful synchronization event; it will either return a valid time or throw.
+     *
+     * <p>Note that synchronization may occur using an insecure network protocol, so the returned
+     * time should not be used for security purposes. The device may resynchronize with the same or
+     * different network source at any time. Due to network delays, variations between servers, or
+     * local (client side) clock drift, the accuracy of the returned times cannot be guaranteed. In
+     * extreme cases, consecutive calls to {@link Clock#millis()} on the returned {@link Clock}
+     * could return times that are out of order.
      *
      * @throws DateTimeException when no network time can be provided.
      */
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index e53873b..89b727c 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -21,13 +21,10 @@
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodRedirect;
-import android.ravenwood.annotation.RavenwoodRedirectionClass;
 import android.util.Log;
 import android.util.MutableInt;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.ravenwood.RavenwoodEnvironment;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -40,8 +37,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Predicate;
 
 /**
  * Gives access to the system properties store.  The system properties
@@ -58,7 +53,6 @@
  */
 @SystemApi
 @RavenwoodKeepWholeClass
-@RavenwoodRedirectionClass("SystemProperties_host")
 public class SystemProperties {
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
@@ -76,7 +70,7 @@
 
     @UnsupportedAppUsage
     @GuardedBy("sChangeCallbacks")
-    static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
+    private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
 
     @GuardedBy("sRoReads")
     private static final HashMap<String, MutableInt> sRoReads =
@@ -102,19 +96,6 @@
         }
     }
 
-    /** @hide */
-    @RavenwoodRedirect
-    public static void init$ravenwood(Map<String, String> values,
-            Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) {
-        throw RavenwoodEnvironment.notSupportedOnDevice();
-    }
-
-    /** @hide */
-    @RavenwoodRedirect
-    public static void reset$ravenwood() {
-        throw RavenwoodEnvironment.notSupportedOnDevice();
-    }
-
     // The one-argument version of native_get used to be a regular native function. Nowadays,
     // we use the two-argument form of native_get all the time, but we can't just delete the
     // one-argument overload: apps use it via reflection, as the UnsupportedAppUsage annotation
@@ -126,46 +107,34 @@
 
     @FastNative
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    @RavenwoodRedirect
     private static native String native_get(String key, String def);
     @FastNative
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    @RavenwoodRedirect
     private static native int native_get_int(String key, int def);
     @FastNative
     @UnsupportedAppUsage
-    @RavenwoodRedirect
     private static native long native_get_long(String key, long def);
     @FastNative
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    @RavenwoodRedirect
     private static native boolean native_get_boolean(String key, boolean def);
 
     @FastNative
-    @RavenwoodRedirect
     private static native long native_find(String name);
     @FastNative
-    @RavenwoodRedirect
     private static native String native_get(long handle);
     @CriticalNative
-    @RavenwoodRedirect
     private static native int native_get_int(long handle, int def);
     @CriticalNative
-    @RavenwoodRedirect
     private static native long native_get_long(long handle, long def);
     @CriticalNative
-    @RavenwoodRedirect
     private static native boolean native_get_boolean(long handle, boolean def);
 
     // _NOT_ FastNative: native_set performs IPC and can block
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    @RavenwoodRedirect
     private static native void native_set(String key, String def);
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
-    @RavenwoodRedirect
     private static native void native_add_change_callback();
-    @RavenwoodRedirect
     private static native void native_report_sysprop_change();
 
     /**
@@ -301,7 +270,7 @@
     }
 
     @SuppressWarnings("unused")  // Called from native code.
-    static void callChangeCallbacks() {
+    private static void callChangeCallbacks() {
         ArrayList<Runnable> callbacks = null;
         synchronized (sChangeCallbacks) {
             //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
@@ -327,6 +296,16 @@
     }
 
     /**
+     * Clear all callback changes.
+     * @hide
+     */
+    public static void clearChangeCallbacksForTest() {
+        synchronized (sChangeCallbacks) {
+            sChangeCallbacks.clear();
+        }
+    }
+
+    /**
      * Notifies listeners that a system property has changed
      * @hide
      */
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index cfbf528..a5697fb 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static android.os.Trace.TRACE_TAG_VIBRATOR;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -138,14 +140,14 @@
             Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
             return;
         }
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason=" + reason);
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "vibrate");
         try {
             mService.vibrate(uid, mContext.getDeviceId(), opPkg, effect, attributes, reason,
                     mToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -155,14 +157,14 @@
             Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager service.");
             return;
         }
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "performHapticFeedback, reason=" + reason);
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedback");
         try {
             mService.performHapticFeedback(mUid, mContext.getDeviceId(), mPackageName, constant,
                     reason, flags, privFlags);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to perform haptic feedback.", e);
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -174,15 +176,14 @@
                             + " no vibrator manager service.");
             return;
         }
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR,
-                "performHapticFeedbackForInputDevice, reason=" + reason);
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedbackForInputDevice");
         try {
             mService.performHapticFeedbackForInputDevice(mUid, mContext.getDeviceId(), mPackageName,
                     constant, inputDeviceId, inputSource, reason, flags, privFlags);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to perform haptic feedback for input device.", e);
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1ca4574..461f1e0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3706,9 +3706,13 @@
      * @hide
      */
     @TestApi
+    @UserHandleAware(
+            requiresAnyOfPermissionsIfNotCaller = {
+                    android.Manifest.permission.MANAGE_USERS,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
     public int getMainDisplayIdAssignedToUser() {
         try {
-            return mService.getMainDisplayIdAssignedToUser();
+            return mService.getMainDisplayIdAssignedToUser(mUserId);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index f670601..a1bfe39 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -224,3 +224,11 @@
      is_exported: true
      bug: "366598445"
 }
+
+flag {
+    name: "network_time_uses_shared_memory"
+    namespace: "system_performance"
+    description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory"
+    bug: "361329788"
+    is_exported: true
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index b0791e3..bca5bcc 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -251,17 +251,18 @@
 }
 
 flag {
-    name: "replace_body_sensors_permission_enabled"
-    is_exported: true
-    namespace: "android_health_services"
-    description: "This flag is used to enable replacing permission BODY_SENSORS(and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE(and READ_HEALTH_DATA_IN_BACKGROUND)"
-    bug: "364638912"
-}
-
-flag {
     name: "appop_access_tracking_logging_enabled"
     is_fixed_read_only: true
     namespace: "permissions"
     description: "Enables logging of the AppOp access tracking"
     bug: "365584286"
 }
+
+flag {
+    name: "replace_body_sensor_permission_enabled"
+    is_fixed_read_only: true
+    is_exported: true
+    namespace: "android_health_services"
+    description: "This fixed read-only flag is used to enable replacing permission BODY_SENSORS (and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE (and READ_HEALTH_DATA_IN_BACKGROUND)"
+    bug: "364638912"
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a622810..27b1dfb 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3046,6 +3046,11 @@
              * <li> {@link #DEFAULT_ACCOUNT_STATE_CLOUD}: The default account is set to a
              * cloud-synced account. New raw contacts requested for insertion without a specified
              * {@link Account} will be saved in the default cloud account. </li>
+             * <li> {@link #DEFAULT_ACCOUNT_STATE_SIM}: The default account is set to a
+             * account that is associated with one of
+             * {@link SimContacts#getSimAccounts(ContentResolver)}. New raw contacts requested
+             * for insertion without a specified {@link Account} will be
+             * saved in this SIM account. </li>
              * </ul>
              */
             @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
@@ -3063,44 +3068,51 @@
                 public static final int DEFAULT_ACCOUNT_STATE_CLOUD = 3;
 
                 /**
+                 * A state indicating that the default account is set as an account that is
+                 * associated with one of {@link SimContacts#getSimAccounts(ContentResolver)}.
+                 */
+                public static final int DEFAULT_ACCOUNT_STATE_SIM = 4;
+
+                /**
                  * The state of the default account. One of
                  * {@link #DEFAULT_ACCOUNT_STATE_NOT_SET},
-                 * {@link #DEFAULT_ACCOUNT_STATE_LOCAL} or
-                 * {@link #DEFAULT_ACCOUNT_STATE_CLOUD}.
+                 * {@link #DEFAULT_ACCOUNT_STATE_LOCAL},
+                 * {@link #DEFAULT_ACCOUNT_STATE_CLOUD}
+                 * {@link #DEFAULT_ACCOUNT_STATE_SIM}.
                  */
                 @DefaultAccountState
                 private final int mState;
 
                 /**
-                 * The account of the default account, when {@link mState} is {
+                 * The account of the default account, when {@link #mState} is {
                  *
-                 * @link #STATE_SET_TO_CLOUD}, or null otherwise.
+                 * @link #DEFAULT_ACCOUNT_STATE_CLOUD} or {@link #DEFAULT_ACCOUNT_STATE_SIM}, or
+                 * null otherwise.
                  */
-                private final Account mCloudAccount;
+                private final Account mAccount;
 
                 /**
                  * Constructs a new `DefaultAccountAndState` instance with the specified state and
                  * cloud
                  * account.
                  *
-                 * @param state        The state of the default account.
-                 * @param cloudAccount The cloud account associated with the default account,
-                 *                     or null if the state is not
-                 *                     {@link #DEFAULT_ACCOUNT_STATE_CLOUD}.
+                 * @param state   The state of the default account.
+                 * @param account The account associated with the default account if the state is
+                 *                {@link #DEFAULT_ACCOUNT_STATE_CLOUD} or
+                 *                {@link #DEFAULT_ACCOUNT_STATE_SIM}, or null otherwise.
                  */
                 public DefaultAccountAndState(@DefaultAccountState int state,
-                        @Nullable Account cloudAccount) {
+                        @Nullable Account account) {
                     if (!isValidDefaultAccountState(state)) {
                         throw new IllegalArgumentException("Invalid default account state.");
                     }
-                    if ((state == DEFAULT_ACCOUNT_STATE_CLOUD) != (cloudAccount != null)) {
+                    if (isCloudOrSimAccount(state) != (account != null)) {
                         throw new IllegalArgumentException(
-                                "Default account can be set to cloud if and only if the cloud "
+                                "Default account can be set to cloud or SIM if and only if the "
                                         + "account is provided.");
                     }
                     this.mState = state;
-                    this.mCloudAccount =
-                            (mState == DEFAULT_ACCOUNT_STATE_CLOUD) ? cloudAccount : null;
+                    this.mAccount = isCloudOrSimAccount(state) ? account : null;
                 }
 
                 /**
@@ -3118,6 +3130,21 @@
                     return new DefaultAccountAndState(DEFAULT_ACCOUNT_STATE_CLOUD, cloudAccount);
                 }
 
+
+                /**
+                 * Creates a `DefaultAccountAndState` instance representing a default account
+                 * that is set to the sim and associated with the specified sim account.
+                 *
+                 * @param simAccount The non-null sim account associated with the default
+                 *                   contacts account.
+                 * @return A new `DefaultAccountAndState` instance with state
+                 * {@link #DEFAULT_ACCOUNT_STATE_SIM}.
+                 */
+                public static @NonNull DefaultAccountAndState ofSim(
+                        @NonNull Account simAccount) {
+                    return new DefaultAccountAndState(DEFAULT_ACCOUNT_STATE_SIM, simAccount);
+                }
+
                 /**
                  * Creates a `DefaultAccountAndState` instance representing a default account
                  * that is set to the local device storage.
@@ -3140,6 +3167,18 @@
                     return new DefaultAccountAndState(DEFAULT_ACCOUNT_STATE_NOT_SET, null);
                 }
 
+                private static boolean isCloudOrSimAccount(@DefaultAccountState int state) {
+                    return state == DEFAULT_ACCOUNT_STATE_CLOUD
+                            || state == DEFAULT_ACCOUNT_STATE_SIM;
+                }
+
+                private static boolean isValidDefaultAccountState(int state) {
+                    return state == DEFAULT_ACCOUNT_STATE_NOT_SET
+                            || state == DEFAULT_ACCOUNT_STATE_LOCAL
+                            || state == DEFAULT_ACCOUNT_STATE_CLOUD
+                            || state == DEFAULT_ACCOUNT_STATE_SIM;
+                }
+
                 /**
                  * @return the state of the default account.
                  */
@@ -3149,16 +3188,17 @@
                 }
 
                 /**
-                 * @return the cloud account associated with the default account, or null if the
-                 * state is not {@link #DEFAULT_ACCOUNT_STATE_CLOUD}.
+                 * @return the cloud account associated with the default account if the
+                 * state is {@link #DEFAULT_ACCOUNT_STATE_CLOUD} or
+                 * {@link #DEFAULT_ACCOUNT_STATE_SIM}.
                  */
-                public @Nullable Account getCloudAccount() {
-                    return mCloudAccount;
+                public @Nullable Account getAccount() {
+                    return mAccount;
                 }
 
                 @Override
                 public int hashCode() {
-                    return Objects.hash(mState, mCloudAccount);
+                    return Objects.hash(mState, mAccount);
                 }
 
                 @Override
@@ -3170,14 +3210,8 @@
                         return false;
                     }
 
-                    return mState == that.mState && Objects.equals(mCloudAccount,
-                            that.mCloudAccount);
-                }
-
-                private static boolean isValidDefaultAccountState(int state) {
-                    return state == DEFAULT_ACCOUNT_STATE_NOT_SET
-                            || state == DEFAULT_ACCOUNT_STATE_LOCAL
-                            || state == DEFAULT_ACCOUNT_STATE_CLOUD;
+                    return mState == that.mState && Objects.equals(mAccount,
+                            that.mAccount);
                 }
 
                 /**
@@ -3189,7 +3223,8 @@
                 @IntDef(
                         prefix = {"DEFAULT_ACCOUNT_STATE_"},
                         value = {DEFAULT_ACCOUNT_STATE_NOT_SET,
-                                DEFAULT_ACCOUNT_STATE_LOCAL, DEFAULT_ACCOUNT_STATE_CLOUD})
+                                DEFAULT_ACCOUNT_STATE_LOCAL, DEFAULT_ACCOUNT_STATE_CLOUD,
+                                DEFAULT_ACCOUNT_STATE_SIM})
                 public @interface DefaultAccountState {
                 }
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b8a8be1..d82af55 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10750,6 +10750,16 @@
                 "lock_screen_show_only_unseen_notifications";
 
         /**
+         * Indicates whether to minimalize the number of notifications to show on the lockscreen.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         *
+         * @hide
+         */
+        public static final String LOCK_SCREEN_NOTIFICATION_MINIMALISM =
+                "lock_screen_notification_minimalism";
+
+        /**
          * Indicates whether snooze options should be shown on notifications
          * <p>
          * Type: int (0 for false, 1 for true)
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index c38ee08..325d274 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -10,3 +10,4 @@
 per-file FileIntegrityManager.java = file:platform/system/security:/fsverity/OWNERS
 per-file IFileIntegrityService.aidl = file:platform/system/security:/fsverity/OWNERS
 per-file *.aconfig = victorhsieh@google.com,eranm@google.com
+per-file *responsible_apis_flags.aconfig = haok@google.com
\ No newline at end of file
diff --git a/core/java/android/security/forensic/OWNERS b/core/java/android/security/forensic/OWNERS
new file mode 100644
index 0000000..d9e82a6
--- /dev/null
+++ b/core/java/android/security/forensic/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 1630302
+
+lizprucka@google.com
+mteffeteller@google.com
+myriamleggieri@google.com
+rmneal@google.com
+wenhaowang@google.com
+willcoster@google.com
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 56d3669..5457bbe 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -24,6 +24,17 @@
 }
 
 flag {
+  name: "asm_reintroduce_grace_period"
+  namespace: "responsible_apis"
+  description: "Allow launches within the grace period for ASM apps"
+  bug: "367702727"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+
+flag {
     name: "content_uri_permission_apis"
     is_exported: true
     namespace: "responsible_apis"
@@ -52,3 +63,11 @@
     description: "Opt the system into enforcement of BAL"
     bug: "339403750"
 }
+
+flag {
+    name: "prevent_intent_redirect"
+    namespace: "responsible_apis"
+    description: "Prevent intent redirect attacks"
+    bug: "361143368"
+    is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 88da8eb..48d7cf7 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -18,6 +18,7 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -90,10 +91,11 @@
             = "android.service.notification.NotificationAssistantService";
 
     /**
-     * Activity Action: Show notification assistant detail setting page in NAS app.
+     * Activity Action: Show notification assistant detail setting page in the NAS app.
      * <p>
-     * In some cases, a matching Activity may not exist, so ensure you
-     * safeguard against this.
+     * To be implemented by the NAS to offer users additional customization of intelligence
+     * features. If the action is not implemented, the OS will not provide a link to it in the
+     * Settings UI.
      * <p>
      * Input: Nothing.
      * <p>
@@ -103,6 +105,30 @@
     public static final String ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS =
             "android.service.notification.action.NOTIFICATION_ASSISTANT_DETAIL_SETTINGS";
 
+    /**
+     * Activity Action: Open notification assistant feedback page in the NAS app.
+     * <p>
+     * If the NAS does not implement this page, the OS will not show any feedback calls to action in
+     * the UI.
+     * <p>
+     * Input: {@link #EXTRA_NOTIFICATION_KEY}, the {@link StatusBarNotification#getKey()} of the
+     * notification the user wants to file feedback for.
+     * <p>
+     * Output: Nothing.
+     */
+    @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS =
+            "android.service.notification.action.NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS";
+
+    /**
+     * A string extra containing the key of the notification that the user triggered feedback for.
+     *
+     * Extra for {@link #ACTION_NOTIFICATION_ASSISTANT_FEEDBACK_SETTINGS}.
+     */
+    @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+    public static final String EXTRA_NOTIFICATION_KEY
+            = "android.service.notification.extra.NOTIFICATION_KEY";
 
     /**
      * Data type: int, the feedback rating score provided by user. The score can be any integer
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d45b24e..303197d 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -202,10 +202,8 @@
     private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
 
     public static final String MANUAL_RULE_ID = "MANUAL_RULE";
-    public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
+    public static final String EVENTS_OBSOLETE_RULE_ID = "EVENTS_DEFAULT_RULE";
     public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
-    public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
-            EVENTS_DEFAULT_RULE_ID);
 
     public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
             Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
@@ -424,21 +422,10 @@
         return policy;
     }
 
+    @FlaggedApi(Flags.FLAG_MODES_UI)
     public static ZenModeConfig getDefaultConfig() {
         ZenModeConfig config = new ZenModeConfig();
 
-        EventInfo eventInfo = new EventInfo();
-        eventInfo.reply = REPLY_YES_OR_MAYBE;
-        ZenRule events = new ZenRule();
-        events.id = EVENTS_DEFAULT_RULE_ID;
-        events.conditionId = toEventConditionId(eventInfo);
-        events.component = ComponentName.unflattenFromString(
-                "android/com.android.server.notification.EventConditionProvider");
-        events.enabled = false;
-        events.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        events.pkg = "android";
-        config.automaticRules.put(EVENTS_DEFAULT_RULE_ID, events);
-
         ScheduleInfo scheduleInfo = new ScheduleInfo();
         scheduleInfo.days = new int[] {1, 2, 3, 4, 5, 6, 7};
         scheduleInfo.startHour = 22;
@@ -457,6 +444,13 @@
         return config;
     }
 
+    // TODO: b/368247671 - Can be made a constant again when modes_ui is inlined
+    public static List<String> getDefaultRuleIds() {
+        return Flags.modesUi()
+            ? List.of(EVERY_NIGHT_DEFAULT_RULE_ID)
+            : List.of(EVERY_NIGHT_DEFAULT_RULE_ID, EVENTS_OBSOLETE_RULE_ID);
+    }
+
     void ensureManualZenRule() {
         if (manualRule == null) {
             final ZenRule newRule = new ZenRule();
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 384add5..2ab16e9 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -2397,7 +2397,11 @@
                 // it hasn't changed and there is no need to update.
                 ret = mBlastBufferQueue.createSurface();
             } else {
-                mBlastBufferQueue.update(mBbqSurfaceControl, width, height, format);
+                if (mBbqSurfaceControl != null && mBbqSurfaceControl.isValid()) {
+                    mBlastBufferQueue.update(mBbqSurfaceControl, width, height, format);
+                } else {
+                    Log.w(TAG, "Skipping BlastBufferQueue update - invalid surface control");
+                }
             }
 
             return ret;
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index c83285a..3599332 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -168,3 +168,13 @@
   description: "Decouple variation settings, weight and style information from Typeface class"
   bug: "361260253"
 }
+
+flag {
+  name: "handwriting_track_disabled"
+  namespace: "text"
+  description: "Handwriting initiator tracks focused view even if handwriting is disabled to fix initiation bug."
+  bug: "361256391"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index ab9bd1f..f132963 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static com.android.text.flags.Flags.handwritingCursorPosition;
+import static com.android.text.flags.Flags.handwritingTrackDisabled;
 import static com.android.text.flags.Flags.handwritingUnsupportedMessage;
 
 import android.annotation.FlaggedApi;
@@ -352,7 +353,7 @@
 
         final View focusedView = getFocusedView();
 
-        if (!view.isAutoHandwritingEnabled()) {
+        if (!handwritingTrackDisabled() && !view.isAutoHandwritingEnabled()) {
             clearFocusedView(focusedView);
             return;
         }
@@ -363,7 +364,8 @@
         updateFocusedView(view);
 
         if (mState != null && mState.mPendingFocusedView != null
-                && mState.mPendingFocusedView.get() == view) {
+                && mState.mPendingFocusedView.get() == view
+                && (!handwritingTrackDisabled() || view.isAutoHandwritingEnabled())) {
             startHandwriting(view);
         }
     }
@@ -416,7 +418,7 @@
      */
     @VisibleForTesting
     public boolean updateFocusedView(@NonNull View view) {
-        if (!view.shouldInitiateHandwriting()) {
+        if (!handwritingTrackDisabled() && !view.shouldInitiateHandwriting()) {
             mFocusedView = null;
             return false;
         }
@@ -424,8 +426,10 @@
         final View focusedView = getFocusedView();
         if (focusedView != view) {
             mFocusedView = new WeakReference<>(view);
-            // A new view just gain focus. By default, we should show hover icon for it.
-            mShowHoverIconForConnectedView = true;
+            if (!handwritingTrackDisabled() || view.shouldInitiateHandwriting()) {
+                // A new view just gain focus. By default, we should show hover icon for it.
+                mShowHoverIconForConnectedView = true;
+            }
         }
 
         return true;
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index e90b1c0..229e8ee7 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -26,7 +26,6 @@
 import android.os.IBinder;
 import android.os.Trace;
 import android.util.proto.ProtoOutputStream;
-import android.view.SurfaceControl.Transaction;
 import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InputMethodManager;
@@ -34,8 +33,6 @@
 import com.android.internal.inputmethod.ImeTracing;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 
-import java.util.function.Supplier;
-
 /**
  * Controls the visibility and animations of IME window insets source.
  * @hide
@@ -54,10 +51,8 @@
      */
     private boolean mIsRequestedVisibleAwaitingLeash;
 
-    public ImeInsetsSourceConsumer(
-            int id, InsetsState state, Supplier<Transaction> transactionSupplier,
-            InsetsController controller) {
-        super(id, WindowInsets.Type.ime(), state, transactionSupplier, controller);
+    public ImeInsetsSourceConsumer(int id, InsetsState state, InsetsController controller) {
+        super(id, WindowInsets.Type.ime(), state, controller);
     }
 
     @Override
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 04bb609..a0d8a17 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -54,13 +54,6 @@
     void notifyFinished(InsetsAnimationControlRunner runner, boolean shown);
 
     /**
-     * Apply the new params to the surface.
-     * @param params The {@link android.view.SyncRtSurfaceTransactionApplier.SurfaceParams} to
-     *               apply.
-     */
-    void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params);
-
-    /**
      * Post a message to release the Surface, guaranteed to happen after all
      * previous calls to applySurfaceParams.
      */
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 91e9230..97facc1 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -99,6 +99,7 @@
     private final @InsetsType int mTypes;
     private @InsetsType int mControllingTypes;
     private final InsetsAnimationControlCallbacks mController;
+    private final SurfaceParamsApplier mSurfaceParamsApplier;
     private final WindowInsetsAnimation mAnimation;
     private final long mDurationMs;
     private final Interpolator mInterpolator;
@@ -123,6 +124,7 @@
     public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls,
             @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetsType int types, InsetsAnimationControlCallbacks controller,
+            SurfaceParamsApplier surfaceParamsApplier,
             InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
             CompatibilityInfo.Translator translator, @Nullable ImeTracker.Token statsToken) {
@@ -131,6 +133,7 @@
         mTypes = types;
         mControllingTypes = types;
         mController = controller;
+        mSurfaceParamsApplier = surfaceParamsApplier;
         mInitialInsetsState = new InsetsState(state, true /* copySources */);
         if (frame != null) {
             final SparseIntArray idSideMap = new SparseIntArray();
@@ -258,6 +261,11 @@
     }
 
     @Override
+    public SurfaceParamsApplier getSurfaceParamsApplier() {
+        return mSurfaceParamsApplier;
+    }
+
+    @Override
     @Nullable
     public ImeTracker.Token getStatsToken() {
         return mStatsToken;
@@ -305,7 +313,7 @@
         updateLeashesForSide(SIDE_RIGHT, offset.right, params, outState, mPendingAlpha);
         updateLeashesForSide(SIDE_BOTTOM, offset.bottom, params, outState, mPendingAlpha);
 
-        mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
+        mSurfaceParamsApplier.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
         mCurrentInsets = mPendingInsets;
         mAnimation.setFraction(mPendingFraction);
         mCurrentAlpha = mPendingAlpha;
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
index 8cb8b47..4f102da 100644
--- a/core/java/android/view/InsetsAnimationControlRunner.java
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -77,6 +77,11 @@
     @AnimationType int getAnimationType();
 
     /**
+     * @return The {@link SurfaceParamsApplier} this runner is using.
+     */
+    SurfaceParamsApplier getSurfaceParamsApplier();
+
+    /**
      * @return The token tracking the current IME request or {@code null} otherwise.
      */
     @Nullable
@@ -99,4 +104,27 @@
      * @param fieldId FieldId of the implementation class
      */
     void dumpDebug(ProtoOutputStream proto, long fieldId);
+
+    /**
+     * Interface applying given surface operations.
+     */
+    interface SurfaceParamsApplier {
+
+        SurfaceParamsApplier DEFAULT = params -> {
+            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            for (int i = params.length - 1; i >= 0; i--) {
+                SyncRtSurfaceTransactionApplier.applyParams(t, params[i], new float[9]);
+            }
+            t.apply();
+            t.close();
+        };
+
+        /**
+         * Apply the new params to the surface.
+         *
+         * @param params The {@link SyncRtSurfaceTransactionApplier.SurfaceParams} to apply.
+         */
+        void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params);
+
+    }
 }
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index fc185bc..8c2c495 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import static android.view.InsetsController.DEBUG;
-import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
 
 import android.annotation.Nullable;
 import android.annotation.UiThread;
@@ -30,7 +29,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsController.AnimationType;
 import android.view.InsetsController.LayoutInsetsDuringAnimation;
-import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation.Bounds;
 import android.view.inputmethod.ImeTracker;
@@ -50,8 +48,6 @@
     private final InsetsAnimationControlCallbacks mCallbacks =
             new InsetsAnimationControlCallbacks() {
 
-        private final float[] mTmpFloat9 = new float[9];
-
         @Override
         @UiThread
         public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController>
@@ -81,19 +77,6 @@
         }
 
         @Override
-        public void applySurfaceParams(SurfaceParams... params) {
-            if (DEBUG) Log.d(TAG, "applySurfaceParams");
-            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-            for (int i = params.length - 1; i >= 0; i--) {
-                SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
-                applyParams(t, surfaceParams, mTmpFloat9);
-            }
-            t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
-            t.apply();
-            t.close();
-        }
-
-        @Override
         public void releaseSurfaceControlFromRt(SurfaceControl sc) {
             if (DEBUG) Log.d(TAG, "releaseSurfaceControlFromRt");
             // Since we don't push the SurfaceParams to the RT we can release directly
@@ -106,6 +89,22 @@
         }
     };
 
+    private SurfaceParamsApplier mSurfaceParamsApplier = new SurfaceParamsApplier() {
+
+        private final float[] mTmpFloat9 = new float[9];
+
+        @Override
+        public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            for (int i = params.length - 1; i >= 0; i--) {
+                SyncRtSurfaceTransactionApplier.applyParams(t, params[i], mTmpFloat9);
+            }
+            t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+            t.apply();
+            t.close();
+        }
+    };
+
     @UiThread
     public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls,
             @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener,
@@ -117,8 +116,8 @@
         mMainThreadHandler = mainThreadHandler;
         mOuterCallbacks = controller;
         mControl = new InsetsAnimationControlImpl(controls, frame, state, listener, types,
-                mCallbacks, insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
-                translator, statsToken);
+                mCallbacks, mSurfaceParamsApplier, insetsAnimationSpec, animationType,
+                layoutInsetsDuringAnimation, translator, statsToken);
         InsetsAnimationThread.getHandler().post(() -> {
             if (mControl.isCancelled()) {
                 return;
@@ -187,6 +186,11 @@
     }
 
     @Override
+    public SurfaceParamsApplier getSurfaceParamsApplier() {
+        return mSurfaceParamsApplier;
+    }
+
+    @Override
     public void updateLayoutInsetsDuringAnimation(
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
         InsetsAnimationThread.getHandler().post(
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8fdf91a..8ac5532 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -29,7 +29,6 @@
 import static android.view.WindowInsets.Type.ime;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import static com.android.window.flags.Flags.insetsControlSeq;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -55,7 +54,6 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSourceConsumer.ShowResult;
-import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation.Bounds;
@@ -85,7 +83,8 @@
  * Implements {@link WindowInsetsController} on the client.
  * @hide
  */
-public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
+public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks,
+        InsetsAnimationControlRunner.SurfaceParamsApplier {
 
     private int mTypesBeingCancelled;
 
@@ -307,7 +306,6 @@
     }
 
     /** Not running an animation. */
-    @VisibleForTesting
     public static final int ANIMATION_TYPE_NONE = -1;
 
     /** Running animation will show insets */
@@ -317,11 +315,9 @@
     public static final int ANIMATION_TYPE_HIDE = 1;
 
     /** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */
-    @VisibleForTesting(visibility = PACKAGE)
     public static final int ANIMATION_TYPE_USER = 2;
 
     /** Running animation will resize insets */
-    @VisibleForTesting
     public static final int ANIMATION_TYPE_RESIZE = 3;
 
     @Retention(RetentionPolicy.SOURCE)
@@ -757,11 +753,9 @@
     public InsetsController(Host host) {
         this(host, (controller, id, type) -> {
             if (!Flags.refactorInsetsController() &&  type == ime()) {
-                return new ImeInsetsSourceConsumer(id, controller.mState,
-                        Transaction::new, controller);
+                return new ImeInsetsSourceConsumer(id, controller.mState, controller);
             } else {
-                return new InsetsSourceConsumer(id, type, controller.mState,
-                        Transaction::new, controller);
+                return new InsetsSourceConsumer(id, type, controller.mState, controller);
             }
         }, host.getHandler());
     }
@@ -882,9 +876,7 @@
         @InsetsType int visibleTypes = 0;
         @InsetsType int[] cancelledUserAnimationTypes = {0};
         for (int i = 0, size = newState.sourceSize(); i < size; i++) {
-            final InsetsSource source = insetsControlSeq()
-                    ? new InsetsSource(newState.sourceAt(i))
-                    : newState.sourceAt(i);
+            final InsetsSource source = new InsetsSource(newState.sourceAt(i));
             @InsetsType int type = source.getType();
             @AnimationType int animationType = getAnimationType(type);
             final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId());
@@ -1525,9 +1517,15 @@
                         insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
                         mHost.getTranslator(), mHost.getHandler(), statsToken)
                 : new InsetsAnimationControlImpl(controls,
-                        frame, mState, listener, typesReady, this, insetsAnimationSpec,
+                        frame, mState, listener, typesReady, this, this, insetsAnimationSpec,
                         animationType, layoutInsetsDuringAnimation, mHost.getTranslator(),
                         statsToken);
+        for (int i = controls.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = mSourceConsumers.get(controls.keyAt(i));
+            if (consumer != null) {
+                consumer.setSurfaceParamsApplier(runner.getSurfaceParamsApplier());
+            }
+        }
         if ((typesReady & WindowInsets.Type.ime()) != 0) {
             ImeTracing.getInstance().triggerClientDump("InsetsAnimationControlImpl",
                     mHost.getInputMethodManager(), null /* icProto */);
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index f90b841..5262751 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -94,6 +94,11 @@
     }
 
     @Override
+    public SurfaceParamsApplier getSurfaceParamsApplier() {
+        return SurfaceParamsApplier.DEFAULT;
+    }
+
+    @Override
     @Nullable
     public ImeTracker.Token getStatsToken() {
         // Return null as resizing the IME view is not explicitly tracked.
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 391d757..da788a7 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
 import static android.view.InsetsController.AnimationType;
 import static android.view.InsetsController.DEBUG;
 import static android.view.InsetsSourceConsumerProto.ANIMATION_STATE;
@@ -28,16 +29,16 @@
 import static android.view.InsetsSourceConsumerProto.TYPE_NUMBER;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import static com.android.window.flags.Flags.insetsControlSeq;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
-import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
@@ -48,7 +49,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
-import java.util.function.Supplier;
 
 /**
  * Controls the visibility and animations of a single window insets source.
@@ -92,10 +92,12 @@
     private final int mType;
 
     private static final String TAG = "InsetsSourceConsumer";
-    private final Supplier<Transaction> mTransactionSupplier;
     @Nullable
     private InsetsSourceControl mSourceControl;
     private boolean mHasWindowFocus;
+    private InsetsAnimationControlRunner.SurfaceParamsApplier mSurfaceParamsApplier =
+            InsetsAnimationControlRunner.SurfaceParamsApplier.DEFAULT;
+    private final Matrix mTmpMatrix = new Matrix();
 
     /**
      * Whether the view has focus returned by {@link #onWindowFocusGained(boolean)}.
@@ -108,16 +110,13 @@
      * @param id The ID of the consumed insets.
      * @param type The {@link InsetsType} of the consumed insets.
      * @param state The current {@link InsetsState} of the consumed insets.
-     * @param transactionSupplier The source of new {@link Transaction} instances. The supplier
-     *         must provide *new* instances, which will be explicitly closed by this class.
      * @param controller The {@link InsetsController} to use for insets interaction.
      */
     public InsetsSourceConsumer(int id, @InsetsType int type, InsetsState state,
-            Supplier<Transaction> transactionSupplier, InsetsController controller) {
+            InsetsController controller) {
         mId = id;
         mType = type;
         mState = state;
-        mTransactionSupplier = transactionSupplier;
         mController = controller;
     }
 
@@ -162,6 +161,9 @@
             if (localVisible != serverVisible) {
                 mController.notifyVisibilityChanged();
             }
+
+            // Reset the applier to the default one which has the most lightweight implementation.
+            setSurfaceParamsApplier(InsetsAnimationControlRunner.SurfaceParamsApplier.DEFAULT);
         } else {
             final boolean requestedVisible = isRequestedVisibleAwaitingControl();
             final SurfaceControl oldLeash = lastControl != null ? lastControl.getLeash() : null;
@@ -184,10 +186,11 @@
                     mController.notifyVisibilityChanged();
                 }
 
-                // If we have a new leash, make sure visibility is up-to-date, even though we
-                // didn't want to run an animation above.
-                if (mController.getAnimationType(mType) == ANIMATION_TYPE_NONE) {
-                    applyRequestedVisibilityToControl();
+                // If there is no animation controlling the leash, make sure the visibility and the
+                // position is up-to-date. Note: ANIMATION_TYPE_RESIZE doesn't control the leash.
+                final int animType = mController.getAnimationType(mType);
+                if (animType == ANIMATION_TYPE_NONE || animType == ANIMATION_TYPE_RESIZE) {
+                    applyRequestedVisibilityAndPositionToControl();
                 }
 
                 // Remove the surface that owned by last control when it lost.
@@ -229,6 +232,15 @@
     }
 
     /**
+     * Sets the SurfaceParamsApplier that the latest animation runner is using. The leash owned by
+     * this class is always applied by the applier, so that the transaction order can always be
+     * aligned with the calling sequence.
+     */
+    void setSurfaceParamsApplier(InsetsAnimationControlRunner.SurfaceParamsApplier applier) {
+        mSurfaceParamsApplier = applier;
+    }
+
+    /**
      * Called right after the animation is started or finished.
      */
     @VisibleForTesting(visibility = PACKAGE)
@@ -418,9 +430,6 @@
 
         // Frame is changing while animating. Keep note of the new frame but keep existing frame
         // until animation is finished.
-        if (!insetsControlSeq()) {
-            newSource = new InsetsSource(newSource);
-        }
         mPendingFrame = new Rect(newSource.getFrame());
         mPendingVisibleFrame = newSource.getVisibleFrame() != null
                 ? new Rect(newSource.getVisibleFrame())
@@ -431,24 +440,30 @@
         if (DEBUG) Log.d(TAG, "updateSource: " + newSource);
     }
 
-    private void applyRequestedVisibilityToControl() {
-        if (mSourceControl == null || mSourceControl.getLeash() == null) {
+    private void applyRequestedVisibilityAndPositionToControl() {
+        if (mSourceControl == null) {
+            return;
+        }
+        final SurfaceControl leash = mSourceControl.getLeash();
+        if (leash == null) {
             return;
         }
 
-        final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
-        try (Transaction t = mTransactionSupplier.get()) {
-            if (DEBUG) Log.d(TAG, "applyRequestedVisibilityToControl: " + requestedVisible);
-            if (requestedVisible) {
-                t.show(mSourceControl.getLeash());
-            } else {
-                t.hide(mSourceControl.getLeash());
-            }
-            // Ensure the alpha value is aligned with the actual requested visibility.
-            t.setAlpha(mSourceControl.getLeash(), requestedVisible ? 1 : 0);
-            t.apply();
-        }
-        onPerceptible(requestedVisible);
+        final boolean visible = (mController.getRequestedVisibleTypes() & mType) != 0;
+        final Point surfacePosition = mSourceControl.getSurfacePosition();
+
+        if (DEBUG) Log.d(TAG, "applyRequestedVisibilityAndPositionToControl: visible=" + visible
+                + " position=" + surfacePosition);
+
+        mTmpMatrix.setTranslate(surfacePosition.x, surfacePosition.y);
+        mSurfaceParamsApplier.applySurfaceParams(
+                new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(leash)
+                        .withVisibility(visible)
+                        .withAlpha(visible ? 1 : 0)
+                        .withMatrix(mTmpMatrix)
+                        .build());
+
+        onPerceptible(visible);
     }
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 15a4715..5c41516 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -146,7 +146,7 @@
                 forceConsumingTypes |= type;
             }
 
-            if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isEnabled()
+            if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue()
                     && (flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) {
                 forceConsumingOpaqueCaptionBar = true;
             }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2dda835..90ceb44 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4525,6 +4525,21 @@
         }
 
         /**
+         * TODO(b/366484871): To be removed once we have some logging in native
+         * This is called when BlastBufferQueue.mergeWithNextTransaction() is called from java, and
+         * for the purposes of logging that path.
+         */
+        void onMergeWithNextTransaction(CharSequence windowName) {
+            if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+                SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+                        "merge", this, null, "window=" + windowName);
+                if (mCalls != null) {
+                    mCalls.clear();
+                }
+            }
+        }
+
+        /**
          * Equivalent to reparent with a null parent, in that it removes
          * the SurfaceControl from the scene, but it also releases
          * the local resources (by calling {@link SurfaceControl#release})
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 33e7905..66776ce 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -130,8 +130,6 @@
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
 import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
-import static com.android.window.flags.Flags.insetsControlChangedItem;
-import static com.android.window.flags.Flags.insetsControlSeq;
 import static com.android.window.flags.Flags.setScPropertiesInClient;
 import static com.android.window.flags.Flags.systemUiImmersiveConfirmationDialog;
 
@@ -889,10 +887,7 @@
     /** Non-{@code null} if {@link #mActivityConfigCallback} is not {@code null}. */
     @Nullable
     private ActivityWindowInfo mLastReportedActivityWindowInfo;
-    @Nullable
-    private final ClientWindowFrames mLastReportedFrames = insetsControlSeq()
-            ? new ClientWindowFrames()
-            : null;
+    private final ClientWindowFrames mLastReportedFrames = new ClientWindowFrames();
     private int mLastReportedInsetsStateSeq = getInitSeq();
     private int mLastReportedActiveControlsSeq = getInitSeq();
 
@@ -2317,9 +2312,6 @@
     }
 
     private void onClientWindowFramesChanged(@NonNull ClientWindowFrames inOutFrames) {
-        if (mLastReportedFrames == null) {
-            return;
-        }
         if (isIncomingSeqStale(mLastReportedFrames.seq, inOutFrames.seq)) {
             // If the incoming is stale, use the last reported instead.
             inOutFrames.setTo(mLastReportedFrames);
@@ -2330,14 +2322,12 @@
     }
 
     private void onInsetsStateChanged(@NonNull InsetsState insetsState) {
-        if (insetsControlSeq()) {
-            if (isIncomingSeqStale(mLastReportedInsetsStateSeq, insetsState.getSeq())) {
-                // The incoming is stale. Skip.
-                return;
-            }
-            // Keep track of the latest.
-            mLastReportedInsetsStateSeq = insetsState.getSeq();
+        if (isIncomingSeqStale(mLastReportedInsetsStateSeq, insetsState.getSeq())) {
+            // The incoming is stale. Skip.
+            return;
         }
+        // Keep track of the latest.
+        mLastReportedInsetsStateSeq = insetsState.getSeq();
 
         if (mTranslator != null) {
             mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
@@ -2352,15 +2342,13 @@
             return;
         }
 
-        if (insetsControlSeq()) {
-            if (isIncomingSeqStale(mLastReportedActiveControlsSeq, activeControls.getSeq())) {
-                // The incoming is stale. Skip.
-                activeControls.release();
-                return;
-            }
-            // Keep track of the latest.
-            mLastReportedActiveControlsSeq = activeControls.getSeq();
+        if (isIncomingSeqStale(mLastReportedActiveControlsSeq, activeControls.getSeq())) {
+            // The incoming is stale. Skip.
+            activeControls.release();
+            return;
         }
+        // Keep track of the latest.
+        mLastReportedActiveControlsSeq = activeControls.getSeq();
 
         final InsetsSourceControl[] controls = activeControls.get();
         if (mTranslator != null) {
@@ -3214,10 +3202,10 @@
             typesToShow |= Type.navigationBars();
         }
         if (captionIsHiddenByFlags && !captionWasHiddenByFlags
-                && ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
+                && ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()) {
             typesToHide |= Type.captionBar();
         } else if (!captionIsHiddenByFlags && captionWasHiddenByFlags
-                && ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
+                && ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()) {
             typesToShow |= Type.captionBar();
         }
         if (typesToHide != 0) {
@@ -11519,12 +11507,8 @@
         public void insetsControlChanged(InsetsState insetsState,
                 InsetsSourceControl.Array activeControls) {
             final boolean isFromInsetsControlChangeItem;
-            if (insetsControlChangedItem()) {
-                isFromInsetsControlChangeItem = mIsFromTransactionItem;
-                mIsFromTransactionItem = false;
-            } else {
-                isFromInsetsControlChangeItem = false;
-            }
+            isFromInsetsControlChangeItem = mIsFromTransactionItem;
+            mIsFromTransactionItem = false;
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor == null) {
                 if (isFromInsetsControlChangeItem) {
@@ -12407,6 +12391,7 @@
         transaction.setBlurRegions(surfaceControl, regionCopy);
 
         if (mBlastBufferQueue != null) {
+            transaction.onMergeWithNextTransaction(getTitle());
             mBlastBufferQueue.mergeWithNextTransaction(transaction, frameNumber);
         }
     }
@@ -12433,6 +12418,9 @@
      */
     public void mergeWithNextTransaction(Transaction t, long frameNumber) {
         if (mBlastBufferQueue != null) {
+            if (t != null) {
+                t.onMergeWithNextTransaction(getTitle());
+            }
             mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
         } else {
             t.apply();
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index dda3993..d5ccca9 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -157,10 +157,10 @@
             // which prevents overlap with the DisplayCutout.
             if (!attachedInParent && !floatingInScreenWindow) {
                 mTempRect.set(outParentFrame);
-                outParentFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+                intersectOrClamp(outParentFrame, displayCutoutSafeExceptMaybeBars);
                 frames.isParentFrameClippedByDisplayCutout = !mTempRect.equals(outParentFrame);
             }
-            outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
+            intersectOrClamp(outDisplayFrame, displayCutoutSafeExceptMaybeBars);
         }
 
         final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
@@ -283,6 +283,19 @@
                 + " requestedInvisibleTypes=" + WindowInsets.Type.toString(~requestedVisibleTypes));
     }
 
+    /**
+     * If both rectangles intersect, set inOutRect to that intersection. Otherwise, clamp inOutRect
+     * to the side (or the corner) that the other rectangle is away from.
+     * Unlike {@link Rect#intersectUnchecked(Rect)}, this method guarantees that the new rectangle
+     * is valid and contained in inOutRect if rectangles involved are valid.
+     */
+    private static void intersectOrClamp(Rect inOutRect, Rect other) {
+        inOutRect.left = Math.min(Math.max(inOutRect.left, other.left), inOutRect.right);
+        inOutRect.top = Math.min(Math.max(inOutRect.top, other.top), inOutRect.bottom);
+        inOutRect.right = Math.max(Math.min(inOutRect.right, other.right), inOutRect.left);
+        inOutRect.bottom = Math.max(Math.min(inOutRect.bottom, other.bottom), inOutRect.top);
+    }
+
     public static void extendFrameByCutout(Rect displayCutoutSafe,
             Rect displayFrame, Rect inOutFrame, Rect tempRect) {
         if (displayCutoutSafe.contains(inOutFrame)) {
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index c88048c..1f341ca 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_KEY_GESTURE_TRIGGERED;
 
 import android.annotation.IntDef;
 import android.os.PowerManager;
@@ -35,6 +36,7 @@
     int FLAG_VIRTUAL = 0x00000002;
 
     int FLAG_INJECTED_FROM_ACCESSIBILITY = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY;
+    int FLAG_KEY_GESTURE_TRIGGERED = POLICY_FLAG_KEY_GESTURE_TRIGGERED;
     int FLAG_INJECTED = 0x01000000;
     int FLAG_TRUSTED = 0x02000000;
     int FLAG_FILTERED = 0x04000000;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index c92593f..7b6e070 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -880,10 +880,6 @@
      * @hide
      */
     public static String typeToString(int type) {
-        if (Flags.enableTypeWindowControl() && type == TYPE_WINDOW_CONTROL) {
-            return "TYPE_WINDOW_CONTROL";
-        }
-
         switch (type) {
             case TYPE_APPLICATION: {
                 return "TYPE_APPLICATION";
@@ -903,8 +899,12 @@
             case TYPE_MAGNIFICATION_OVERLAY: {
                 return "TYPE_MAGNIFICATION_OVERLAY";
             }
-            default:
+            default: {
+                if (Flags.enableTypeWindowControl() && type == TYPE_WINDOW_CONTROL) {
+                    return "TYPE_WINDOW_CONTROL";
+                }
                 return "<UNKNOWN:" + type + ">";
+            }
         }
     }
 
diff --git a/core/java/android/window/flags/DesktopModeFlags.java b/core/java/android/window/flags/DesktopModeFlags.java
index 701b6be..944a106 100644
--- a/core/java/android/window/flags/DesktopModeFlags.java
+++ b/core/java/android/window/flags/DesktopModeFlags.java
@@ -43,11 +43,28 @@
     // All desktop mode related flags to be overridden by developer option toggle will be added here
     ENABLE_DESKTOP_WINDOWING_MODE(
             Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true),
-    ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false),
+    ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
     ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(
             Flags::enableCaptionCompatInsetForceConsumption, true),
     ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS(
-            Flags::enableCaptionCompatInsetForceConsumptionAlways, true);
+            Flags::enableCaptionCompatInsetForceConsumptionAlways, true),
+    ENABLE_CASCADING_WINDOWS(Flags::enableCascadingWindows, true),
+    ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(
+            Flags::enableDesktopWindowingWallpaperActivity, true),
+    ENABLE_DESKTOP_WINDOWING_MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true),
+    ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
+    ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
+    ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
+    ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
+    ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
+    DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
+    ENABLE_WINDOWING_SCALED_RESIZING(Flags::enableWindowingScaledResizing, true),
+    ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
+    ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
+    ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
+    ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(
+            Flags::enableDesktopWindowingTaskbarRunningApps, true),
+    ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false);
 
     private static final String TAG = "DesktopModeFlagsUtil";
     // Function called to obtain aconfig flag value.
@@ -68,7 +85,7 @@
      * Determines state of flag based on the actual flag and desktop mode developer option
      * overrides.
      */
-    public boolean isEnabled() {
+    public boolean isTrue() {
         Application application = ActivityThread.currentApplication();
         if (!Flags.showDesktopWindowingDevOption()
                 || !mShouldOverrideByDevOption
@@ -112,12 +129,13 @@
     }
 
     /** Override state of desktop mode developer option toggle. */
-    private enum ToggleOverride {
+    public enum ToggleOverride {
         OVERRIDE_UNSET,
         OVERRIDE_OFF,
         OVERRIDE_ON;
 
-        int getSetting() {
+        /** Returns the integer representation of this {@code ToggleOverride}. */
+        public int getSetting() {
             return switch (this) {
                 case OVERRIDE_ON -> 1;
                 case OVERRIDE_OFF -> 0;
@@ -125,7 +143,8 @@
             };
         }
 
-        static ToggleOverride fromSetting(int setting, @Nullable ToggleOverride fallback) {
+        /** Returns the {@code ToggleOverride} corresponding to a given integer setting. */
+        public static ToggleOverride fromSetting(int setting, @Nullable ToggleOverride fallback) {
             return switch (setting) {
                 case 1 -> OVERRIDE_ON;
                 case 0 -> OVERRIDE_OFF;
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index cc5e583..fbc30ed 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -287,6 +287,13 @@
 }
 
 flag {
+    name: "enable_fully_immersive_in_desktop"
+    namespace: "lse_desktop_experience"
+    description: "Enabled the fully immersive experience from desktop"
+    bug: "359523924"
+}
+
+flag {
     name: "enable_display_focus_in_shell_transitions"
     namespace: "lse_desktop_experience"
     description: "Creates a shell transition when display focus switches."
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index e1402f8..ccaaf63 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -70,6 +70,17 @@
 }
 
 flag {
+  name: "common_surface_animator"
+  namespace: "windowing_frontend"
+  description: "A reusable surface animator for default transition"
+  bug: "326331384"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "reduce_keyguard_transitions"
   namespace: "windowing_frontend"
   description: "Avoid setting keyguard transitions ready unless there are no other changes"
@@ -111,6 +122,17 @@
 }
 
 flag {
+    name: "remove_activity_starter_dream_callback"
+    namespace: "windowing_frontend"
+    description: "Avoid a race with DreamManagerService callbacks for isDreaming by checking Activity state directly"
+    bug: "366452352"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+    is_fixed_read_only: true
+}
+
+flag {
     name: "supports_multi_instance_system_ui"
     is_exported: true
     namespace: "multitasking"
@@ -266,3 +288,14 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "remove_starting_window_wait_for_multi_transitions"
+  namespace: "windowing_frontend"
+  description: "Avoid remove starting window too early when playing multiple transitions"
+  bug: "362347290"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 9ae3fc1..f0ea7a8 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -68,27 +68,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "insets_control_changed_item"
-    description: "Pass insetsControlChanged through ClientTransaction to fix the racing"
-    bug: "339380439"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-    namespace: "windowing_sdk"
-    name: "insets_control_seq"
-    description: "Add seqId to InsetsControls to ensure the stale update is ignored"
-    bug: "339380439"
-    is_fixed_read_only: true
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-    namespace: "windowing_sdk"
      name: "move_animation_options_to_change"
      description: "Move AnimationOptions from TransitionInfo to each Change"
      bug: "327332488"
diff --git a/core/java/com/android/internal/os/ApplicationSharedMemory.java b/core/java/com/android/internal/os/ApplicationSharedMemory.java
new file mode 100644
index 0000000..84f713e
--- /dev/null
+++ b/core/java/com/android/internal/os/ApplicationSharedMemory.java
@@ -0,0 +1,296 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.time.DateTimeException;
+
+/**
+ * This class is used to create and access a shared memory region.
+ *
+ * <p>The intended use case is that memory is shared between system processes and application
+ * processes such that it's readable to all apps and writable only to system processes.
+ *
+ * <p>This shared memory region can be used as an alternative to Binder IPC for driving
+ * communication between system processes and application processes at a lower latency and higher
+ * throughput than Binder IPC can provide, under circumstances where the additional features of
+ * Binder IPC are not required.
+ *
+ * <p>Unlike Binder IPC, shared memory doesn't support synchronous transactions and associated
+ * ordering guarantees, client identity (and therefore caller permission checking), and access
+ * auditing. Therefore it's not a suitable alternative to Binder IPC for most use cases.
+ *
+ * <p>Additionally, because the intended use case is to make this shared memory region readable to
+ * all apps, it's not suitable for sharing sensitive data.
+ *
+ * @see {@link ApplicationSharedMemoryTestRule} for unit testing support.
+ * @hide
+ */
+public class ApplicationSharedMemory implements AutoCloseable {
+
+    // LINT.IfChange(invalid_network_time)
+    public static final long INVALID_NETWORK_TIME = -1;
+    // LINT.ThenChange(frameworks/base/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp:invalid_network_time)
+
+    private static final boolean DEBUG = false;
+    private static final String LOG_TAG = "ApplicationSharedMemory";
+
+    @VisibleForTesting public static ApplicationSharedMemory sInstance;
+
+    /** Get the process-global instance. */
+    public static ApplicationSharedMemory getInstance() {
+        ApplicationSharedMemory instance = sInstance;
+        if (instance == null) {
+            throw new IllegalStateException("ApplicationSharedMemory not initialized");
+        }
+        return instance;
+    }
+
+    /** Set the process-global instance. */
+    public static void setInstance(ApplicationSharedMemory instance) {
+        if (DEBUG) {
+            Log.d(LOG_TAG, "setInstance: " + instance);
+        }
+
+        if (sInstance != null) {
+            throw new IllegalStateException("ApplicationSharedMemory already initialized");
+        }
+        sInstance = instance;
+    }
+
+    /** Allocate mutable shared memory region. */
+    public static ApplicationSharedMemory create() {
+        if (DEBUG) {
+            Log.d(LOG_TAG, "create");
+        }
+
+        int fd = nativeCreate();
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        fileDescriptor.setInt$(fd);
+
+        final boolean mutable = true;
+        long ptr = nativeMap(fd, mutable);
+        nativeInit(ptr);
+
+        return new ApplicationSharedMemory(fileDescriptor, mutable, ptr);
+    }
+
+    /**
+     * Open shared memory region from a given {@link FileDescriptor}.
+     *
+     * @param fileDescriptor Handle to shared memory region.
+     * @param mutable Whether the shared memory region is mutable. If true, will be mapped as
+     *     read-write memory. If false, will be mapped as read-only memory. Passing true (mutable)
+     *     if |pfd| is a handle to read-only memory will result in undefined behavior.
+     */
+    public static ApplicationSharedMemory fromFileDescriptor(
+            @NonNull FileDescriptor fileDescriptor, boolean mutable) {
+        if (DEBUG) {
+            Log.d(LOG_TAG, "fromFileDescriptor: " + fileDescriptor + " mutable: " + mutable);
+        }
+
+        long ptr = nativeMap(fileDescriptor.getInt$(), mutable);
+        return new ApplicationSharedMemory(fileDescriptor, mutable, ptr);
+    }
+
+    /**
+     * Allocate read-write shared memory region.
+     *
+     * @return File descriptor of the shared memory region.
+     */
+    private static native int nativeCreate();
+
+    /**
+     * Map the shared memory region.
+     *
+     * @param fd File descriptor of the shared memory region.
+     * @param isMutable Whether the shared memory region is mutable. If true, will be mapped as
+     *     read-write memory. If false, will be mapped as read-only memory.
+     * @return Pointer to the mapped shared memory region.
+     */
+    private static native long nativeMap(int fd, boolean isMutable);
+
+    /**
+     * Initialize read-write shared memory region.
+     *
+     * @param Pointer to the mapped shared memory region.
+     */
+    private static native void nativeInit(long ptr);
+
+    /**
+     * Unmap the shared memory region.
+     *
+     * @param ptr Pointer to the mapped shared memory region.
+     */
+    private static native void nativeUnmap(long ptr);
+
+    /**
+     * If true, this object owns the read-write instance of the shared memory region. If false, this
+     * object can only read.
+     */
+    private final boolean mMutable;
+
+    /**
+     * Handle to the shared memory region. This can be send to other processes over Binder calls or
+     * Intent extras. Recipients can use this handle to obtain read-only access to the shared memory
+     * region.
+     */
+    private FileDescriptor mFileDescriptor;
+
+    /** Native pointer to the mapped shared memory region. */
+    private volatile long mPtr;
+
+    ApplicationSharedMemory(@NonNull FileDescriptor fileDescriptor, boolean mutable, long ptr) {
+        mFileDescriptor = fileDescriptor;
+        mMutable = mutable;
+        mPtr = ptr;
+    }
+
+    /**
+     * Returns the file descriptor of the shared memory region.
+     *
+     * <p>This file descriptor retains the mutability properties of this object instance, and can be
+     * sent over Binder IPC or Intent extras to another process to allow the remote process to map
+     * the same shared memory region with the same access rights.
+     *
+     * @throws IllegalStateException if the file descriptor is closed.
+     */
+    public FileDescriptor getFileDescriptor() {
+        checkFileOpen();
+        return mFileDescriptor;
+    }
+
+    /**
+     * Returns a read-only file descriptor of the shared memory region. This object can be sent over
+     * Binder IPC or Intent extras to another process to allow the remote process to map the same
+     * shared memory region with read-only access.
+     *
+     * @return a read-only handle to the shared memory region.
+     * @throws IllegalStateException if the file descriptor is closed.
+     */
+    public FileDescriptor getReadOnlyFileDescriptor() throws IOException {
+        checkFileOpen();
+        FileDescriptor readOnlyFileDescriptor = new FileDescriptor();
+        int readOnlyFd = nativeDupAsReadOnly(mFileDescriptor.getInt$());
+        readOnlyFileDescriptor.setInt$(readOnlyFd);
+        return readOnlyFileDescriptor;
+    }
+
+    /** Return a read-only duplicate of the file descriptor. */
+    private static native int nativeDupAsReadOnly(int fd);
+
+    /** Set the latest network Unix Epoch minus realtime millis. */
+    public void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(long offset) {
+        checkMutable();
+        nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(mPtr, offset);
+    }
+
+    /** Clear the latest network Unix Epoch minus realtime millis. */
+    public void clearLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() {
+        checkMutable();
+        nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(
+                mPtr, INVALID_NETWORK_TIME);
+    }
+
+    @CriticalNative
+    private static native void nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(
+            long ptr, long offset);
+
+    /**
+     * Get the latest network Unix Epoch minus realtime millis.
+     *
+     * @throws DateTimeException when no network time can be provided.
+     */
+    public long getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis()
+            throws DateTimeException {
+        checkMapped();
+        long offset = nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(mPtr);
+        if (offset == INVALID_NETWORK_TIME) {
+            throw new DateTimeException("No network time available");
+        }
+        return offset;
+    }
+
+    @CriticalNative
+    public static native long nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(
+            long ptr);
+
+    /**
+     * Close the associated file descriptor.
+     *
+     * <p>This method is safe to call if you never intend to pass the file descriptor to another
+     * process, whether via {@link #getFileDescriptor()} or {@link #getReadOnlyFileDescriptor()}.
+     * After calling this method, subsequent calls to {@link #getFileDescriptor()} or {@link
+     * #getReadOnlyFileDescriptor()} will throw an {@link IllegalStateException}.
+     */
+    public void closeFileDescriptor() {
+        if (mFileDescriptor != null) {
+            IoUtils.closeQuietly(mFileDescriptor);
+            mFileDescriptor = null;
+        }
+    }
+
+    public void close() {
+        if (mPtr != 0) {
+            nativeUnmap(mPtr);
+            mPtr = 0;
+        }
+
+        if (mFileDescriptor != null) {
+            IoUtils.closeQuietly(mFileDescriptor);
+            mFileDescriptor = null;
+        }
+    }
+
+    private void checkFileOpen() {
+        if (mFileDescriptor == null) {
+            throw new IllegalStateException("File descriptor is closed");
+        }
+    }
+
+    /**
+     * Check that the shared memory region is mapped.
+     *
+     * @throws IllegalStateException if the shared memory region is not mapped.
+     */
+    private void checkMapped() {
+        if (mPtr == 0) {
+            throw new IllegalStateException("Instance is closed");
+        }
+    }
+
+    /**
+     * Check that the shared memory region is mapped and mutable.
+     *
+     * @throws IllegalStateException if the shared memory region is not mapped or not mutable.
+     */
+    private void checkMutable() {
+        checkMapped();
+        if (!mMutable) {
+            throw new IllegalStateException("Not mutable");
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 391d257..ffd4499 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -15,3 +15,6 @@
 # ANRs
 # Bug component : 158088 = per-file TimeoutRecord.java
 per-file TimeoutRecord.java = file:/PERFORMANCE_OWNERS
+
+# ApplicationSharedMemory
+per-file *ApplicationSharedMemory* = file:/PERFORMANCE_OWNERS
diff --git a/core/java/com/android/internal/os/anr/OWNERS b/core/java/com/android/internal/os/anr/OWNERS
index 9816752..1ad642f 100644
--- a/core/java/com/android/internal/os/anr/OWNERS
+++ b/core/java/com/android/internal/os/anr/OWNERS
@@ -1,3 +1,2 @@
 benmiles@google.com
-gaillard@google.com
 mohamadmahmoud@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index c7117e9..07df248 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -27,4 +27,12 @@
     description: "If the debug store is enabled."
     bug: "314735374"
     is_fixed_read_only: true
+}
+
+flag {
+    name: "application_shared_memory_enabled"
+    namespace: "system_performance"
+    description: "Whether ApplicationSharedMemory is enabled."
+    bug: "365575551"
+    is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 12d3264..032ac42 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -3025,6 +3025,7 @@
     @Override
     public PackageImpl setSplitCodePaths(@Nullable String[] splitCodePaths) {
         this.splitCodePaths = splitCodePaths;
+        this.mSplits = null; // reset for paths changed
         if (splitCodePaths != null) {
             int size = splitCodePaths.length;
             for (int index = 0; index < size; index++) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 84dfc49..6faea17 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1219,14 +1219,14 @@
         final boolean hideCaptionBar = fullscreen
                 || (requestedVisibleTypes & WindowInsets.Type.captionBar()) == 0;
         final boolean consumingCaptionBar =
-                ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()
+                ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()
                         && ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0
                         && hideCaptionBar);
 
         final boolean isOpaqueCaptionBar = customizableWindowHeaders()
                 && (appearance & APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) == 0;
         final boolean consumingOpaqueCaptionBar =
-                ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isEnabled()
+                ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue()
                         && mLastForceConsumingOpaqueCaptionBar
                         && isOpaqueCaptionBar;
 
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index fbc058c..b0e38e2 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -122,18 +122,20 @@
     private final Lock mBackgroundServiceLock = new ReentrantLock();
     private ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor();
 
-    public PerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups) {
+    public PerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups)
+            throws ServiceManager.ServiceNotFoundException {
         this(null, null, null, () -> {}, groups);
     }
 
-    public PerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups) {
+    public PerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups)
+            throws ServiceManager.ServiceNotFoundException {
         this(null, null, null, cacheUpdater, groups);
     }
 
     public PerfettoProtoLogImpl(
             @NonNull String viewerConfigFilePath,
             @NonNull Runnable cacheUpdater,
-            @NonNull IProtoLogGroup[] groups) {
+            @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException {
         this(viewerConfigFilePath,
                 null,
                 new ProtoLogViewerConfigReader(() -> {
@@ -177,12 +179,14 @@
             @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
             @Nullable ProtoLogViewerConfigReader viewerConfigReader,
             @NonNull Runnable cacheUpdater,
-            @NonNull IProtoLogGroup[] groups) {
+            @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException {
         this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader,
                 cacheUpdater, groups,
                 ProtoLogDataSource::new,
-                IProtoLogConfigurationService.Stub
-                        .asInterface(ServiceManager.getService(PROTOLOG_CONFIGURATION_SERVICE))
+                android.tracing.Flags.clientSideProtoLogging() ?
+                    IProtoLogConfigurationService.Stub.asInterface(
+                        ServiceManager.getServiceOrThrow(PROTOLOG_CONFIGURATION_SERVICE)
+                    ) : null
         );
     }
 
@@ -222,7 +226,7 @@
         if (android.tracing.Flags.clientSideProtoLogging()) {
             mProtoLogConfigurationService = configurationService;
             Objects.requireNonNull(mProtoLogConfigurationService,
-                    "ServiceManager returned a null ProtoLog Configuration Service");
+                    "A null ProtoLog Configuration Service was provided!");
 
             try {
                 var args = new ProtoLogConfigurationServiceImpl.RegisterClientArgs();
diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java
index bf77db7..adf03fe 100644
--- a/core/java/com/android/internal/protolog/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/ProtoLog.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.protolog;
 
+import android.os.ServiceManager;
+
 import com.android.internal.protolog.common.IProtoLog;
 import com.android.internal.protolog.common.IProtoLogGroup;
 import com.android.internal.protolog.common.LogLevel;
@@ -76,7 +78,11 @@
                     groups = allGroups.toArray(new IProtoLogGroup[0]);
                 }
 
-                sProtoLogInstance = new PerfettoProtoLogImpl(groups);
+                try {
+                    sProtoLogInstance = new PerfettoProtoLogImpl(groups);
+                } catch (ServiceManager.ServiceNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
             }
         } else {
             sProtoLogInstance = new LogcatOnlyProtoLogImpl();
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 7bdcf2d..5d67534 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -23,6 +23,7 @@
 import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH;
 
 import android.annotation.Nullable;
+import android.os.ServiceManager;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -106,18 +107,23 @@
             final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]);
 
             if (android.tracing.Flags.perfettoProtologTracing()) {
-                File f = new File(sViewerConfigPath);
-                if (!ProtoLog.REQUIRE_PROTOLOGTOOL && !f.exists()) {
-                    // TODO(b/353530422): Remove - temporary fix to unblock b/352290057
-                    // In some tests the viewer config file might not exist in which we don't
-                    // want to provide config path to the user
-                    Log.w(LOG_TAG, "Failed to find viewerConfigFile when setting up "
-                            + ProtoLogImpl.class.getSimpleName() + ". "
-                            + "Setting up without a viewer config instead...");
-                    sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups);
-                } else {
-                    sServiceInstance =
-                            new PerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups);
+                try {
+                    File f = new File(sViewerConfigPath);
+                    if (!ProtoLog.REQUIRE_PROTOLOGTOOL && !f.exists()) {
+                        // TODO(b/353530422): Remove - temporary fix to unblock b/352290057
+                        // In some tests the viewer config file might not exist in which we don't
+                        // want to provide config path to the user
+                        Log.w(LOG_TAG, "Failed to find viewerConfigFile when setting up "
+                                + ProtoLogImpl.class.getSimpleName() + ". "
+                                + "Setting up without a viewer config instead...");
+
+                        sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups);
+                    } else {
+                        sServiceInstance =
+                                new PerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups);
+                    }
+                } catch (ServiceManager.ServiceNotFoundException e) {
+                    throw new RuntimeException(e);
                 }
             } else {
                 var protologImpl = new LegacyProtoLogImpl(
diff --git a/core/java/com/android/internal/util/ScreenshotRequest.java b/core/java/com/android/internal/util/ScreenshotRequest.java
index c8b7def..702e5e2 100644
--- a/core/java/com/android/internal/util/ScreenshotRequest.java
+++ b/core/java/com/android/internal/util/ScreenshotRequest.java
@@ -33,6 +33,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
+import android.view.Display;
 import android.view.WindowManager;
 
 import java.util.Objects;
@@ -53,11 +54,12 @@
     private final Bitmap mBitmap;
     private final Rect mBoundsInScreen;
     private final Insets mInsets;
+    private final int mDisplayId;
 
     private ScreenshotRequest(
             @WindowManager.ScreenshotType int type, @WindowManager.ScreenshotSource int source,
             ComponentName topComponent, int taskId, int userId,
-            Bitmap bitmap, Rect boundsInScreen, Insets insets) {
+            Bitmap bitmap, Rect boundsInScreen, Insets insets, int displayId) {
         mType = type;
         mSource = source;
         mTopComponent = topComponent;
@@ -66,6 +68,7 @@
         mBitmap = bitmap;
         mBoundsInScreen = boundsInScreen;
         mInsets = insets;
+        mDisplayId = displayId;
     }
 
     ScreenshotRequest(Parcel in) {
@@ -77,6 +80,7 @@
         mBitmap = HardwareBitmapBundler.bundleToHardwareBitmap(in.readTypedObject(Bundle.CREATOR));
         mBoundsInScreen = in.readTypedObject(Rect.CREATOR);
         mInsets = in.readTypedObject(Insets.CREATOR);
+        mDisplayId = in.readInt();
     }
 
     @WindowManager.ScreenshotType
@@ -113,6 +117,10 @@
         return mTopComponent;
     }
 
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -128,6 +136,7 @@
         dest.writeTypedObject(HardwareBitmapBundler.hardwareBitmapToBundle(mBitmap), 0);
         dest.writeTypedObject(mBoundsInScreen, 0);
         dest.writeTypedObject(mInsets, 0);
+        dest.writeInt(mDisplayId);
     }
 
     @NonNull
@@ -161,6 +170,7 @@
         private int mTaskId = INVALID_TASK_ID;
         private int mUserId = USER_NULL;
         private ComponentName mTopComponent;
+        private int mDisplayId = Display.INVALID_DISPLAY;
 
         /**
          * Begin building a ScreenshotRequest.
@@ -193,7 +203,7 @@
             }
 
             return new ScreenshotRequest(mType, mSource, mTopComponent, mTaskId, mUserId, mBitmap,
-                    mBoundsInScreen, mInsets);
+                    mBoundsInScreen, mInsets, mDisplayId);
         }
 
         /**
@@ -255,6 +265,16 @@
             mInsets = insets;
             return this;
         }
+
+        /**
+         * Set the display ID for this request.
+         *
+         * @param displayId see {@link Display}
+         */
+        public Builder setDisplayId(int displayId) {
+            mDisplayId = displayId;
+            return this;
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index e65b4b6..c0a7383 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -16,14 +16,19 @@
 
 package com.android.internal.widget;
 
+import static java.lang.Float.NaN;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
 import android.graphics.Path;
+import android.graphics.PorterDuff;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.hardware.input.InputManager;
@@ -65,11 +70,14 @@
     private static final PointerState EMPTY_POINTER_STATE = new PointerState();
 
     public static class PointerState {
-        // Trace of previous points.
-        private float[] mTraceX = new float[32];
-        private float[] mTraceY = new float[32];
-        private boolean[] mTraceCurrent = new boolean[32];
-        private int mTraceCount;
+        private float mCurrentX = NaN;
+        private float mCurrentY = NaN;
+        private float mPreviousX = NaN;
+        private float mPreviousY = NaN;
+        private float mFirstX = NaN;
+        private float mFirstY = NaN;
+        private boolean mPreviousPointIsHistorical;
+        private boolean mCurrentPointIsHistorical;
 
         // True if the pointer is down.
         @UnsupportedAppUsage
@@ -96,31 +104,20 @@
         public PointerState() {
         }
 
-        public void clearTrace() {
-            mTraceCount = 0;
-        }
-
-        public void addTrace(float x, float y, boolean current) {
-            int traceCapacity = mTraceX.length;
-            if (mTraceCount == traceCapacity) {
-                traceCapacity *= 2;
-                float[] newTraceX = new float[traceCapacity];
-                System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
-                mTraceX = newTraceX;
-
-                float[] newTraceY = new float[traceCapacity];
-                System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
-                mTraceY = newTraceY;
-
-                boolean[] newTraceCurrent = new boolean[traceCapacity];
-                System.arraycopy(mTraceCurrent, 0, newTraceCurrent, 0, mTraceCount);
-                mTraceCurrent= newTraceCurrent;
+        public void addTrace(float x, float y, boolean isHistorical) {
+            if (Float.isNaN(mFirstX)) {
+                mFirstX = x;
+            }
+            if (Float.isNaN(mFirstY)) {
+                mFirstY = y;
             }
 
-            mTraceX[mTraceCount] = x;
-            mTraceY[mTraceCount] = y;
-            mTraceCurrent[mTraceCount] = current;
-            mTraceCount += 1;
+            mPreviousX = mCurrentX;
+            mPreviousY = mCurrentY;
+            mCurrentX = x;
+            mCurrentY = y;
+            mPreviousPointIsHistorical = mCurrentPointIsHistorical;
+            mCurrentPointIsHistorical = isHistorical;
         }
     }
 
@@ -149,6 +146,12 @@
     private final SparseArray<PointerState> mPointers = new SparseArray<PointerState>();
     private final PointerCoords mTempCoords = new PointerCoords();
 
+    // Draw the trace of all pointers in the current gesture in a separate layer
+    // that is not cleared on every frame so that we don't have to re-draw the
+    // entire trace on each frame.
+    private final Bitmap mTraceBitmap;
+    private final Canvas mTraceCanvas;
+
     private final Region mSystemGestureExclusion = new Region();
     private final Region mSystemGestureExclusionRejected = new Region();
     private final Path mSystemGestureExclusionPath = new Path();
@@ -197,6 +200,10 @@
         mPathPaint.setARGB(255, 0, 96, 255);
         mPathPaint.setStyle(Paint.Style.STROKE);
 
+        mTraceBitmap = Bitmap.createBitmap(getResources().getDisplayMetrics().widthPixels,
+                getResources().getDisplayMetrics().heightPixels, Bitmap.Config.ARGB_8888);
+        mTraceCanvas = new Canvas(mTraceBitmap);
+
         configureDensityDependentFactors();
 
         mSystemGestureExclusionPaint = new Paint();
@@ -256,7 +263,7 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         mTextPaint.getFontMetricsInt(mTextMetrics);
-        mHeaderBottom = mHeaderPaddingTop-mTextMetrics.ascent+mTextMetrics.descent+2;
+        mHeaderBottom = mHeaderPaddingTop - mTextMetrics.ascent + mTextMetrics.descent + 2;
         if (false) {
             Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent
                     + " descent=" + mTextMetrics.descent
@@ -269,6 +276,7 @@
     // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
     // angles less than or greater than 0 radians rotate the major axis left or right.
     private RectF mReusableOvalRect = new RectF();
+
     private void drawOval(Canvas canvas, float x, float y, float major, float minor,
             float angle, Paint paint) {
         canvas.save(Canvas.MATRIX_SAVE_FLAG);
@@ -285,6 +293,8 @@
     protected void onDraw(Canvas canvas) {
         final int NP = mPointers.size();
 
+        canvas.drawBitmap(mTraceBitmap, 0, 0, null);
+
         if (!mSystemGestureExclusion.isEmpty()) {
             mSystemGestureExclusionPath.reset();
             mSystemGestureExclusion.getBoundaryPath(mSystemGestureExclusionPath);
@@ -303,32 +313,9 @@
         // Pointer trace.
         for (int p = 0; p < NP; p++) {
             final PointerState ps = mPointers.valueAt(p);
+            float lastX = ps.mCurrentX, lastY = ps.mCurrentY;
 
-            // Draw path.
-            final int N = ps.mTraceCount;
-            float lastX = 0, lastY = 0;
-            boolean haveLast = false;
-            boolean drawn = false;
-            mPaint.setARGB(255, 128, 255, 255);
-            for (int i=0; i < N; i++) {
-                float x = ps.mTraceX[i];
-                float y = ps.mTraceY[i];
-                if (Float.isNaN(x) || Float.isNaN(y)) {
-                    haveLast = false;
-                    continue;
-                }
-                if (haveLast) {
-                    canvas.drawLine(lastX, lastY, x, y, mPathPaint);
-                    final Paint paint = ps.mTraceCurrent[i - 1] ? mCurrentPointPaint : mPaint;
-                    canvas.drawPoint(lastX, lastY, paint);
-                    drawn = true;
-                }
-                lastX = x;
-                lastY = y;
-                haveLast = true;
-            }
-
-            if (drawn) {
+            if (!Float.isNaN(lastX) && !Float.isNaN(lastY)) {
                 // Draw velocity vector.
                 mPaint.setARGB(255, 255, 64, 128);
                 float xVel = ps.mXVelocity * (1000 / 60);
@@ -353,7 +340,7 @@
                         Math.max(getHeight(), getWidth()), mTargetPaint);
 
                 // Draw current point.
-                int pressureLevel = (int)(ps.mCoords.pressure * 255);
+                int pressureLevel = (int) (ps.mCoords.pressure * 255);
                 mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
                 canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
 
@@ -424,8 +411,7 @@
                 .append(" / ").append(mMaxNumPointers)
                 .toString(), 1, base, mTextPaint);
 
-        final int count = ps.mTraceCount;
-        if ((mCurDown && ps.mCurDown) || count == 0) {
+        if ((mCurDown && ps.mCurDown) || Float.isNaN(ps.mCurrentX)) {
             canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
                     mTextBackgroundPaint);
             canvas.drawText(mText.clear()
@@ -437,8 +423,8 @@
                     .append("Y: ").append(ps.mCoords.y, 1)
                     .toString(), 1 + itemW * 2, base, mTextPaint);
         } else {
-            float dx = ps.mTraceX[count - 1] - ps.mTraceX[0];
-            float dy = ps.mTraceY[count - 1] - ps.mTraceY[0];
+            float dx = ps.mCurrentX - ps.mFirstX;
+            float dy = ps.mCurrentY - ps.mFirstY;
             canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
                     Math.abs(dx) < mVC.getScaledTouchSlop()
                             ? mTextBackgroundPaint : mTextLevelPaint);
@@ -565,9 +551,9 @@
                 .append(" TouchMinor=").append(coords.touchMinor, 3)
                 .append(" ToolMajor=").append(coords.toolMajor, 3)
                 .append(" ToolMinor=").append(coords.toolMinor, 3)
-                .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
+                .append(" Orientation=").append((float) (coords.orientation * 180 / Math.PI), 1)
                 .append("deg")
-                .append(" Tilt=").append((float)(
+                .append(" Tilt=").append((float) (
                         coords.getAxisValue(MotionEvent.AXIS_TILT) * 180 / Math.PI), 1)
                 .append("deg")
                 .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
@@ -598,6 +584,7 @@
                 mCurNumPointers = 0;
                 mMaxNumPointers = 0;
                 mVelocity.clear();
+                mTraceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                 if (mAltVelocity != null) {
                     mAltVelocity.clear();
                 }
@@ -646,7 +633,8 @@
                     logCoords("Pointer", action, i, coords, id, event);
                 }
                 if (ps != null) {
-                    ps.addTrace(coords.x, coords.y, false);
+                    ps.addTrace(coords.x, coords.y, true);
+                    updateDrawTrace(ps);
                 }
             }
         }
@@ -659,7 +647,8 @@
                 logCoords("Pointer", action, i, coords, id, event);
             }
             if (ps != null) {
-                ps.addTrace(coords.x, coords.y, true);
+                ps.addTrace(coords.x, coords.y, false);
+                updateDrawTrace(ps);
                 ps.mXVelocity = mVelocity.getXVelocity(id);
                 ps.mYVelocity = mVelocity.getYVelocity(id);
                 if (mAltVelocity != null) {
@@ -702,13 +691,26 @@
                 if (mActivePointerId == id) {
                     mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
                 }
-                ps.addTrace(Float.NaN, Float.NaN, false);
+                ps.addTrace(Float.NaN, Float.NaN, true);
             }
         }
 
         invalidate();
     }
 
+    private void updateDrawTrace(PointerState ps) {
+        mPaint.setARGB(255, 128, 255, 255);
+        float x = ps.mCurrentX;
+        float y = ps.mCurrentY;
+        float lastX = ps.mPreviousX;
+        float lastY = ps.mPreviousY;
+        if (!Float.isNaN(x) && !Float.isNaN(y) && !Float.isNaN(lastX) && !Float.isNaN(lastY)) {
+            mTraceCanvas.drawLine(lastX, lastY, x, y, mPathPaint);
+            Paint paint = ps.mPreviousPointIsHistorical ? mPaint : mCurrentPointPaint;
+            mTraceCanvas.drawPoint(lastX, lastY, paint);
+        }
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         onPointerEvent(event);
@@ -767,7 +769,7 @@
                 return true;
             default:
                 return KeyEvent.isGamepadButton(keyCode)
-                    || KeyEvent.isModifierKey(keyCode);
+                        || KeyEvent.isModifierKey(keyCode);
         }
     }
 
@@ -887,7 +889,7 @@
         public FasterStringBuilder append(int value, int zeroPadWidth) {
             final boolean negative = value < 0;
             if (negative) {
-                value = - value;
+                value = -value;
                 if (value < 0) {
                     append("-2147483648");
                     return this;
@@ -973,26 +975,27 @@
 
     private ISystemGestureExclusionListener mSystemGestureExclusionListener =
             new ISystemGestureExclusionListener.Stub() {
-        @Override
-        public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion,
-                Region systemGestureExclusionUnrestricted) {
-            Region exclusion = Region.obtain(systemGestureExclusion);
-            Region rejected = Region.obtain();
-            if (systemGestureExclusionUnrestricted != null) {
-                rejected.set(systemGestureExclusionUnrestricted);
-                rejected.op(exclusion, Region.Op.DIFFERENCE);
-            }
-            Handler handler = getHandler();
-            if (handler != null) {
-                handler.post(() -> {
-                    mSystemGestureExclusion.set(exclusion);
-                    mSystemGestureExclusionRejected.set(rejected);
-                    exclusion.recycle();
-                    invalidate();
-                });
-            }
-        }
-    };
+                @Override
+                public void onSystemGestureExclusionChanged(int displayId,
+                        Region systemGestureExclusion,
+                        Region systemGestureExclusionUnrestricted) {
+                    Region exclusion = Region.obtain(systemGestureExclusion);
+                    Region rejected = Region.obtain();
+                    if (systemGestureExclusionUnrestricted != null) {
+                        rejected.set(systemGestureExclusionUnrestricted);
+                        rejected.op(exclusion, Region.Op.DIFFERENCE);
+                    }
+                    Handler handler = getHandler();
+                    if (handler != null) {
+                        handler.post(() -> {
+                            mSystemGestureExclusion.set(exclusion);
+                            mSystemGestureExclusionRejected.set(rejected);
+                            exclusion.recycle();
+                            invalidate();
+                        });
+                    }
+                }
+            };
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 9a4ff8f..9797d96 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -258,6 +258,7 @@
                 "com_android_internal_content_om_OverlayConfig.cpp",
                 "com_android_internal_content_om_OverlayManagerImpl.cpp",
                 "com_android_internal_net_NetworkUtilsInternal.cpp",
+                "com_android_internal_os_ApplicationSharedMemory.cpp",
                 "com_android_internal_os_ClassLoaderFactory.cpp",
                 "com_android_internal_os_DebugStore.cpp",
                 "com_android_internal_os_FuseAppLoop.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 03b5143a..70a80b2 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -201,6 +201,7 @@
 extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
 extern int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env);
 extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
+extern int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv *env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_DebugStore(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
@@ -1516,6 +1517,7 @@
 }
 
 static const RegJNIRec gRegJNI[] = {
+        REG_JNI(register_com_android_internal_os_ApplicationSharedMemory),
         REG_JNI(register_com_android_internal_os_RuntimeInit),
         REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
         REG_JNI(register_android_os_SystemClock),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index c0fe098..af10623 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -116,3 +116,6 @@
 
 # IF Tools
 per-file android_tracing_Perfetto* = file:platform/development:/tools/winscope/OWNERS
+
+# ApplicationSharedMemory
+per-file *ApplicationSharedMemory* = file:/PERFORMANCE_OWNERS
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index a022842..b1221ee 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -190,18 +190,25 @@
         return -1;
     }
 
-    jbyte* bufferBytes = NULL;
-    if (buffer) {
-        bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
+    bool is_dir_in = (endpoint & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN;
+    std::unique_ptr<jbyte[]> bufferBytes(new (std::nothrow) jbyte[length]);
+    if (!bufferBytes) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return -1;
     }
 
-    jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes + start, length, timeout);
-
-    if (bufferBytes) {
-        env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
+    if (!is_dir_in && buffer) {
+        env->GetByteArrayRegion(buffer, start, length, bufferBytes.get());
     }
 
-    return result;
+    jint bytes_transferred =
+            usb_device_bulk_transfer(device, endpoint, bufferBytes.get(), length, timeout);
+
+    if (bytes_transferred > 0 && is_dir_in) {
+        env->SetByteArrayRegion(buffer, start, bytes_transferred, bufferBytes.get());
+    }
+
+    return bytes_transferred;
 }
 
 static jobject
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 88e6fa3..e99d890 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -34,8 +34,6 @@
 
 #if defined(__BIONIC__)
 # include <sys/system_properties.h>
-#else
-struct prop_info;
 #endif
 
 namespace android {
@@ -46,7 +44,6 @@
 template<typename Functor>
 void ReadProperty(const prop_info* prop, Functor&& functor)
 {
-#if defined(__BIONIC__)
     auto thunk = [](void* cookie,
                     const char* /*name*/,
                     const char* value,
@@ -54,9 +51,6 @@
         std::forward<Functor>(*static_cast<Functor*>(cookie))(value);
     };
     __system_property_read_callback(prop, thunk, &functor);
-#else
-    LOG(FATAL) << "fast property access supported only on device";
-#endif
 }
 
 template<typename Functor>
@@ -66,16 +60,11 @@
     if (!key.c_str()) {
         return;
     }
-#if defined(__BIONIC__)
     const prop_info* prop = __system_property_find(key.c_str());
     if (!prop) {
         return;
     }
     ReadProperty(prop, std::forward<Functor>(functor));
-#else
-    std::forward<Functor>(functor)(
-        android::base::GetProperty(key.c_str(), "").c_str());
-#endif
 }
 
 jstring SystemProperties_getSS(JNIEnv* env, jclass clazz, jstring keyJ,
@@ -132,17 +121,12 @@
 
 jlong SystemProperties_find(JNIEnv* env, jclass, jstring keyJ)
 {
-#if defined(__BIONIC__)
     ScopedUtfChars key(env, keyJ);
     if (!key.c_str()) {
         return 0;
     }
     const prop_info* prop = __system_property_find(key.c_str());
     return reinterpret_cast<jlong>(prop);
-#else
-    LOG(FATAL) << "fast property access supported only on device";
-    __builtin_unreachable();  // Silence warning
-#endif
 }
 
 jstring SystemProperties_getH(JNIEnv* env, jclass clazz, jlong propJ)
@@ -198,11 +182,7 @@
     // request" failures).
     errno = 0;
     bool success;
-#if defined(__BIONIC__)
     success = !__system_property_set(key.c_str(), value_c_str);
-#else
-    success = android::base::SetProperty(key.c_str(), value_c_str);
-#endif
     if (!success) {
         if (errno != 0) {
             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 5a444bb..c364451 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -83,7 +83,7 @@
         return 0;
     }
 
-    ResXMLParser* st = new ResXMLParser(*osb);
+    ResXMLParser* st = new(std::nothrow) ResXMLParser(*osb);
     if (st == NULL) {
         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
         return 0;
diff --git a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
new file mode 100644
index 0000000..453e539
--- /dev/null
+++ b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
@@ -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.
+ */
+
+// See: ApplicationSharedMemory.md
+
+#include <cutils/ashmem.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <nativehelper/JNIHelp.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <atomic>
+#include <cstddef>
+#include <new>
+
+#include "core_jni_helpers.h"
+
+namespace {
+
+// Atomics should be safe to use across processes if they are lock free.
+static_assert(std::atomic<int64_t>::is_always_lock_free == true,
+              "atomic<int64_t> is not always lock free");
+
+// This is the data structure that is shared between processes.
+//
+// Tips for extending:
+// - Atomics are safe for cross-process use as they are lock free, if they are accessed as
+//   individual values.
+// - Consider multi-ABI systems, e.g. devices that support launching both 64-bit and 32-bit
+//   app processes. Use fixed-size types (e.g. `int64_t`) to ensure that the data structure is
+//   the same size across all ABIs. Avoid implicit assumptions about struct packing/padding.
+class alignas(8) SharedMemory { // Ensure that `sizeof(SharedMemory)` is the same across 32-bit and
+                                // 64-bit systems.
+private:
+    volatile std::atomic<int64_t> latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis;
+
+    // LINT.IfChange(invalid_network_time)
+    static constexpr int64_t INVALID_NETWORK_TIME = -1;
+    // LINT.ThenChange(frameworks/base/core/java/com/android/internal/os/ApplicationSharedMemory.java:invalid_network_time)
+
+public:
+    // Default constructor sets initial values
+    SharedMemory()
+          : latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(INVALID_NETWORK_TIME) {}
+
+    int64_t getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis() const {
+        return latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis;
+    }
+
+    void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset) {
+        latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset;
+    }
+};
+
+// Update the expected value when modifying the members of SharedMemory.
+// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
+// and 64-bit systems.
+static_assert(sizeof(SharedMemory) == 8, "Unexpected SharedMemory size");
+
+static jint nativeCreate(JNIEnv* env, jclass) {
+    // Create anonymous shared memory region
+    int fd = ashmem_create_region("ApplicationSharedMemory", sizeof(SharedMemory));
+    if (fd < 0) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to create ashmem: %s",
+                             strerror(errno));
+    }
+    return fd;
+}
+
+static jlong nativeMap(JNIEnv* env, jclass, jint fd, jboolean isMutable) {
+    void* ptr = mmap(nullptr, sizeof(SharedMemory), isMutable ? PROT_READ | PROT_WRITE : PROT_READ,
+                     MAP_SHARED, fd, 0);
+    if (ptr == MAP_FAILED) {
+        close(fd);
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to mmap shared memory: %s",
+                             strerror(errno));
+    }
+
+    return reinterpret_cast<jlong>(ptr);
+}
+
+static void nativeInit(JNIEnv* env, jclass, jlong ptr) {
+    new (reinterpret_cast<SharedMemory*>(ptr)) SharedMemory();
+}
+
+static void nativeUnmap(JNIEnv* env, jclass, jlong ptr) {
+    if (munmap(reinterpret_cast<void*>(ptr), sizeof(SharedMemory)) == -1) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                             "Failed to munmap shared memory: %s", strerror(errno));
+    }
+}
+
+static jint nativeDupAsReadOnly(JNIEnv* env, jclass, jint fd) {
+    // Duplicate file descriptor
+    fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+    if (fd < 0) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Failed to dup fd: %s",
+                             strerror(errno));
+    }
+
+    // Set new file descriptor to read-only
+    if (ashmem_set_prot_region(fd, PROT_READ)) {
+        close(fd);
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                             "Failed to ashmem_set_prot_region: %s", strerror(errno));
+    }
+
+    return fd;
+}
+
+static void nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr,
+                                                                                 jlong offset) {
+    SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
+    sharedMemory->setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(offset);
+}
+
+static jlong nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(jlong ptr) {
+    SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
+    return sharedMemory->getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
+}
+
+static const JNINativeMethod gMethods[] = {
+        {"nativeCreate", "()I", (void*)nativeCreate},
+        {"nativeMap", "(IZ)J", (void*)nativeMap},
+        {"nativeInit", "(J)V", (void*)nativeInit},
+        {"nativeUnmap", "(J)V", (void*)nativeUnmap},
+        {"nativeDupAsReadOnly", "(I)I", (void*)nativeDupAsReadOnly},
+        {"nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(JJ)V",
+         (void*)nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
+        {"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J",
+         (void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
+};
+
+} // anonymous namespace
+
+namespace android {
+
+static const char kApplicationSharedMemoryClassName[] =
+        "com/android/internal/os/ApplicationSharedMemory";
+static jclass gApplicationSharedMemoryClass;
+
+int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv* env) {
+    gApplicationSharedMemoryClass =
+            MakeGlobalRefOrDie(env, FindClassOrDie(env, kApplicationSharedMemoryClassName));
+    RegisterMethodsOrDie(env, "com/android/internal/os/ApplicationSharedMemory", gMethods,
+                         NELEM(gMethods));
+    return JNI_OK;
+}
+
+} // namespace android
diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto
index 78cf6f4..8e9f447 100644
--- a/core/proto/android/app/appstartinfo.proto
+++ b/core/proto/android/app/appstartinfo.proto
@@ -41,4 +41,5 @@
     optional AppStartLaunchMode launch_mode = 11;
     optional bool was_force_stopped = 12;
     optional int64 monotonic_creation_time_ms = 13;
+    optional int32 start_component = 14;
 }
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index e7f0560..258832e 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -157,10 +157,8 @@
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     repeated int32 vibrator_ids = 1;
     optional VibrationProto current_vibration = 2;
-    optional bool is_vibrating = 3;
     optional int32 is_vibrator_controller_registered = 27;
     optional VibrationProto current_external_vibration = 4;
-    optional bool vibrator_under_external_control = 5;
     optional bool low_power_mode = 6;
     optional bool vibrate_on = 24;
     reserved 25; // prev keyboard_vibration_on
@@ -183,4 +181,6 @@
     repeated VibrationProto previous_vibrations = 16;
     repeated VibrationParamProto previous_vibration_params = 28;
     reserved 17; // prev previous_external_vibrations
+    reserved 3; // prev is_vibrating, check current_vibration instead
+    reserved 5; // prev vibrator_under_external_control, check current_external_vibration instead
 }
\ No newline at end of file
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index bb654f0..d836563 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -62,6 +62,9 @@
 
     // HWUI renders pipeline type: GL or Vulkan
     optional PipelineType pipeline = 8;
+
+    // The UID of the app
+    optional int32 uid = 9;
 }
 
 message GraphicsStatsJankSummaryProto {
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9352413..1cd2150 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Stel ’n skermslot"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Stel skermslot"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Stel ’n skermslot op dié toestel om jou privaat ruimte te gebruik"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Stel ’n skermslot op dié toestel om privaat ruimte uit te vee"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Program is nie beskikbaar nie"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is nie op die oomblik beskikbaar nie."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> is nie beskikbaar nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 10855c8..484afc3 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ማያ ገጽ መቆለፊያን ያቀናብሩ"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ማያ ገጽ መቆለፊያውን ያቀናብሩ"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"የግል ቦታዎን ለመጠቀም፣ በዚህ መሣሪያ ላይ ማያ ገጽ መቆለፊያን ያቀናብሩ"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"የግል ቦታን ለመሰረዝ በዚህ መሣሪያ ላይ ማያ ገፅ መቆለፊያ ያቀናብሩ"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"መተግበሪያ አይገኝም"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> አሁን አይገኝም።"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> አይገኝም"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 623ef23..75e93d1 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -2014,8 +2014,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ضبط قفل شاشة"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ضبط قفل الشاشة"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"لاستخدام مساحتك الخاصة، يجب ضبط قفل شاشة على هذا الجهاز"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"لحذف المساحة الخاصة، يجب ضبط قفل شاشة على هذا الجهاز"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"التطبيق غير متاح"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> غير متاح الآن."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"تطبيق <xliff:g id="ACTIVITY">%1$s</xliff:g> غير متاح"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index ba1095b..e64c85e 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"এটা স্ক্ৰীন লক ছেট কৰক"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"স্ক্ৰীন লক ছেট কৰা"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"আপোনাৰ প্ৰাইভেট স্পে\'চ ব্যৱহাৰ কৰিবলৈ এই ডিভাইচটোত স্ক্ৰীন লক ছেট কৰক"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"প্ৰাইভেট স্পে’চ মচিবলৈ, এই ডিভাইচটোত এটা স্ক্ৰীন লক ছেট কৰক"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"এপ্‌টো উপলব্ধ নহয়"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"এই মুহূৰ্তত <xliff:g id="APP_NAME">%1$s</xliff:g> উপলব্ধ নহয়।"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> উপলব্ধ নহয়"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 1b574d1..25dc429 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekran kilidi ayarlayın"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ekran kilidi ayarlayın"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Bu cihazda ekran kilidi ayarlamaqla şəxsi sahədən istifadə edin"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Bu cihazda ekran kilidi ayarlamaqla şəxsi sahəni silin"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Tətbiq əlçatan deyil"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hazırda əlçatan deyil."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> əlçatan deyil"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2116a6ef..7413703 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Podesite otključavanje ekrana"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Podesi otključavanje ekrana"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Da biste koristili privatni prostor, podesite otključavanje ekrana na ovom uređaju"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Da biste izbrisali privatan prostor, podesite otključavanje ekrana na ovom uređaju"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – nije dostupno"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0219b6c..a5af4b9 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Наладзьце блакіроўку экрана"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Наладзіць блакіроўку экрана"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Каб выкарыстоўваць прыватную прастору, на прыладзе неабходна наладзіць блакіроўку экрана"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Каб выдаліць прыватную прастору, на прыладзе неабходна наладзіць блакіроўку экрана"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Праграма недаступная"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" цяпер недаступная."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недаступна: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 019068b..0871bfd 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -992,7 +992,7 @@
     <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"ভুল পিন কোড৷"</string>
     <string name="keyguard_label_text" msgid="3841953694564168384">"আনলক করতে, মেনু টিপুন তারপর ০ টিপুন৷"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"জরুরী নম্বর"</string>
-    <string name="lockscreen_carrier_default" msgid="6192313772955399160">"কোনো পরিষেবা নেই"</string>
+    <string name="lockscreen_carrier_default" msgid="6192313772955399160">"কোনও পরিষেবা নেই"</string>
     <string name="lockscreen_screen_locked" msgid="7364905540516041817">"স্ক্রীণ লক করা আছে৷"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"আনলক করতে বা জরুরি কল করতে মেনু টিপুন৷"</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"আনলক করতে মেনু টিপুন৷"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index bdd9f15..50f2221 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -191,7 +191,7 @@
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"Pokušali ste izbrisati previše sadržaja iz kategorije <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
     <string name="low_memory" product="tablet" msgid="5557552311566179924">"Pohrana tableta je puna. Izbrišite fajlove kako biste oslobodili prostor."</string>
     <string name="low_memory" product="watch" msgid="3479447988234030194">"Prostor za gledanje je pun. Izbrišite neke fajlove da oslobodite prostor."</string>
-    <string name="low_memory" product="tv" msgid="6663680413790323318">"Pohrana Android TV uređaja je puna. Izbrišite neke fajlove da oslobodite prostor."</string>
+    <string name="low_memory" product="tv" msgid="6663680413790323318">"Pohrana na uređaju Android TV je puna. Izbrišite neke fajlove da oslobodite prostor."</string>
     <string name="low_memory" product="default" msgid="2539532364144025569">"Pohrana telefona je puna. Izbrišite fajlove kako biste oslobodili prostor."</string>
     <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{CA certifikat je instaliran}one{CA certifikati su instalirani}few{CA certifikati su instalirani}other{CA certifikati su instalirani}}"</string>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Od nepoznate treće strane"</string>
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Postavite zaključavanje ekrana"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Postavite zaključavanje ekrana"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Za upotrebu privatnog prostora postavite zaključavanje ekrana na uređaju"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Da izbrišete privatni prostor, postavite zaključavanje ekrana na uređaju"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"Nedostupno: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
@@ -2066,7 +2065,7 @@
     <string name="app_category_maps" msgid="6395725487922533156">"Mape i navigacija"</string>
     <string name="app_category_productivity" msgid="1844422703029557883">"Produktivnost"</string>
     <string name="app_category_accessibility" msgid="6643521607848547683">"Pristupačnost"</string>
-    <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Memorija uređaja"</string>
+    <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Pohrana na uređaju"</string>
     <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Otklanjanje grešaka putem USB-a"</string>
     <string name="time_picker_hour_label" msgid="4208590187662336864">"sat"</string>
     <string name="time_picker_minute_label" msgid="8307452311269824553">"minuta"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8aa1224..2fea7fb 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Nastavte si zámek obrazovky"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nastavit zámek obrazovky"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Pokud chcete používat soukromý prostor, nastavte na tomto zařízení zámek obrazovky"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Pokud chcete vymazat soukromý prostor, nastavte na tomto zařízení zámek obrazovky"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikace není k dispozici"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> v tuto chvíli není k dispozici."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> není k dispozici"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 95e61b9..a9626a6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Konfigurer en skærmlås"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Konfigurer skærmlås"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Konfigurer en skærmlås på enheden for at bruge dit private område"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Konfigurer en skærmlås på enheden for at slette det private område"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgængelig"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgængelig lige nu."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> er ikke understøttet"</string>
@@ -2154,7 +2153,7 @@
     <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
     <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Deaktiver"</string>
     <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Få flere oplysninger"</string>
-    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Tilpassede Android-notifikationer blev erstattet af forbedrede notifikationer i Android 12. Denne funktion viser foreslåede handlinger og svar samt organiserer dine notifikationer.\n\nForbedrede notifikationer kan få adgang til indhold i notifikationer, bl.a. personlige oplysninger såsom beskeder og navne på kontakter. Funktionen kan også afvise eller svare på notifikationer, f.eks. ved at besvare telefonopkald og justere Forstyr ikke."</string>
+    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Adaptive Android-notifikationer blev erstattet af forbedrede notifikationer i Android 12. Denne funktion viser foreslåede handlinger og svar samt organiserer dine notifikationer.\n\nForbedrede notifikationer kan få adgang til indhold i notifikationer, bl.a. personlige oplysninger såsom beskeder og navne på kontakter. Funktionen kan også afvise eller svare på notifikationer, f.eks. ved at besvare telefonopkald og justere Forstyr ikke."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notifikation med oplysninger om rutinetilstand"</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Batterisparefunktion er aktiveret"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"Reducerer batteriforbruget for at forlænge batteritiden"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 41c1e4b..da0e9bd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Displaysperre einrichten"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Displaysperre einrichten"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Richte zur Nutzung des vertraulichen Profils auf dem Gerät die Displaysperre ein"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Um das vertrauliche Profil zu löschen, richte auf dem Gerät eine Displaysperre ein"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist derzeit nicht verfügbar."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nicht verfügbar"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 12d2756..cbb30fd 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Configurar bloqueo de pantalla"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Configurar bloqueo de pantalla"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar tu espacio privado, configura un bloqueo de pantalla"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Para borrar el espacio privado, configura un bloqueo de pantalla."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"La app no está disponible"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible en este momento."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no disponible"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c6ee8ef..23024f0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Define un bloqueo de pantalla"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Establecer bloqueo de pantalla"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar el espacio privado, define un bloqueo de pantalla"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Para eliminar el espacio privado, define un método de desbloqueo de pantalla en este dispositivo"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"La aplicación no está disponible"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"En estos momentos, <xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> no disponible"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4389b4b..326b3db 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Seadistage ekraanilukk"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Seadistage ekraanilukk"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Seadistage oma privaatse ruumi jaoks seadmele ekraanilukk"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Privaatse ruumi kustutamiseks määrake selles seadmes ekraanilukk"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Rakendus ei ole saadaval"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole praegu saadaval."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ei ole saadaval"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 56b10d1..d092f46 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ezarri pantailaren blokeoa"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ezarri pantailaren blokeoa"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Eremu pribatua erabiltzeko, ezarri pantailaren blokeoa gailuan"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Eremu pribatua ezabatzeko, ezarri pantaila blokeatzeko aukera bat gailuan"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikazioa ez dago erabilgarri"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ez dago erabilgarri une honetan."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ez dago erabilgarri"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 34edd03..e37106d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"قفل صفحه تنظیم کنید"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"تنظیم قفل صفحه"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"برای استفاده از فضای خصوصی، قفل صفحه تنظیم کنید"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"برای حذف کردن فضای خصوصی، قفل صفحه در این دستگاه تنظیم کنید"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"برنامه در دسترس نیست"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال‌حاضر در دسترس نیست."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> دردسترس نیست"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index ecc7a3f..672e508 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Näytön lukituksen asettaminen"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Aseta näytön lukitus"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Edellyttää näytön lukitusta"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Jos haluat poistaa yksityisen tilan, aseta laitteelle näytön lukitus"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Sovellus ei ole käytettävissä"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole nyt käytettävissä."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ei käytettävissä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 86b83a0..bc11e547 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Config. Verrouillage d\'écran"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Config. Verrouillage d\'écran"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Configurez verrouillage de l\'écran pour utiliser Espace privé"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Réglez le verrouillage de l\'écran pour supprimer l\'espace privé"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"L\'appli n\'est pas accessible"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas accessible pour le moment."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non accessible"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 5fa4a3b..03cf463 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Activer verrouillage écran"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Activer verrouillage écran"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Pour utiliser votre espace privé, activez le verrouillage de l\'écran sur cet appareil"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Pour supprimer un espace privé, définissez un verrouillage de l\'écran sur cet appareil."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Application non disponible"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas disponible pour le moment."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponible"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 2f3cffa..430d649 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1937,7 +1937,7 @@
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Noite da semana"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fin de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
-    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Durmindo"</string>
+    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Mentres durmo"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Xestionada por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Activada"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desactivada"</string>
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Define un bloqueo de pantalla"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Define un bloqueo de pantalla"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar o espazo privado, define un bloqueo de pantalla"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Para eliminar o espazo privado, define un bloqueo de pantalla neste dispositivo"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"A aplicación non está dispoñible"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> non está dispoñible neste momento."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non está dispoñible"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index aa190a5..f40fb93 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -642,7 +642,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"એપને તમારા મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવાની મંજૂરી આપે છે."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"બાયોમેટ્રિક્સનો ઉપયોગ કરો"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"બાયોમેટ્રિક્સ અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"તે તમે જ છો એ ચકાસો"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"આ તમે જ છો તેની ચકાસણી કરો"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"આગળ વધવા માટે બાયોમેટ્રિકનો ઉપયોગ કરો"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"ચાલુ રાખવા માટે તમારા બાયોમેટ્રિક ડેટા અથવા સ્ક્રીન લૉક સુવિધાનો ઉપયોગ કરો"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string>
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"સ્ક્રીન લૉક સેટ કરો"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"સ્ક્રીન લૉક સેટ કરો"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"તમારી ખાનગી સ્પેસનો ઉપયોગ કરવા, આ ડિવાઇસ પર સ્ક્રીન લૉક સેટ કરો"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"ખાનગી સ્પેસનો ડિલીટ કરવા, આ ડિવાઇસ પર સ્ક્રીન લૉક સેટ કરો"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ઍપ ઉપલબ્ધ નથી"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> હાલમાં ઉપલબ્ધ નથી."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ઉપલબ્ધ નથી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 4c76cff..0386387f 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"स्क्रीन लॉक सेट करें"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"स्क्रीन लॉक सेट करें"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"प्राइवेट स्पेस के लिए, इस डिवाइस पर स्क्रीन लॉक सेट करें"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"प्राइवेट स्पेस मिटाने के लिए, इस डिवाइस पर स्क्रीन लॉक सेट करें"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> इस समय उपलब्ध नहीं है."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध नहीं है"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index bf972b8..54dc55d 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Postavite zaključavanje zaslona"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Postavi zaključavanje zaslona"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Da biste upotrebljavali privatni prostor, postavite zaključavanje zaslona na ovom uređaju"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Za upotrebu privatnog prostora postavite zaključavanje zaslona."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutačno nije dostupna."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – nije dostupno"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 357328c..179de20 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Állítson be képernyőzárat"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Képernyőzár beállítása"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"A privát terület használatához állítson be képernyőzárat"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"A privát terület törléséhez állítson be képernyőzárat ezen az eszközön."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Az alkalmazás nem hozzáférhető"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg nem hozzáférhető."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"A(z) <xliff:g id="ACTIVITY">%1$s</xliff:g> nem áll rendelkezése"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 5405bb2..312a105 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1937,7 +1937,7 @@
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Աշխատանքային օր"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Շաբաթ-կիրակի"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Միջոցառում"</string>
-    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Քնի ժամանակ"</string>
+    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Քնի ժամ"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Կառավարվում է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Միացված է"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Անջատված է"</string>
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Կարգավորեք էկրանի կողպումը"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Կարգավորել էկրանի կողպումը"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Մասնավոր տարածքն օգտագործելու համար այս սարքում կարգավորեք էկրանի կողպումը"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Մասնավոր տարածքը ջնջելու համար սահմանեք էկրանի կողպում այս սարքում"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Հավելվածը հասանելի չէ"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն այս պահին հասանելի չէ։"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>՝ անհասանելի է"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f5a2a14..e830d6d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Setel kunci layar"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Setel kunci layar"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Untuk menggunakan ruang privasi, setel kunci layar di perangkat ini"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Untuk menghapus ruang privasi, setel kunci layar di perangkat ini"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikasi tidak tersedia"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia saat ini."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> tidak tersedia"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index baeaa3f..307fb52 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Stilltu skjálás"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Stilla skjálás"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Stilltu skjálás í tækinu til að nota leynirými"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Stilltu skjálás í tækinu til að eyða leynirými"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Forrit er ekki tiltækt"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ekki tiltækt núna."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ekki í boði"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 48766b8..08ea1f1 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Imposta un blocco schermo"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Imposta il blocco schermo"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Per utilizzare il tuo spazio privato, imposta un blocco schermo sul dispositivo"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Per eliminare lo spazio privato, imposta un blocco schermo sul dispositivo"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"L\'app non è disponibile"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è al momento disponibile."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non disponibile"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index d3f0d98..1c1918c 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1407,7 +1407,7 @@
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"המכשיר זיהה התקן אודיו אנלוגי"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ההתקן שחיברת לא תואם לטלפון הזה. יש להקיש לקבלת מידע נוסף."</string>
     <string name="adb_active_notification_title" msgid="408390247354560331">"‏ניפוי באגים ב-USB מחובר"</string>
-    <string name="adb_active_notification_message" msgid="5617264033476778211">"‏לכיבוי של ניפוי הבאגים ב-USB, יש להקיש"</string>
+    <string name="adb_active_notification_message" msgid="5617264033476778211">"‏יש להקיש כדי לכבות את ניפוי הבאגים ב-USB"</string>
     <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"‏יש ללחוץ על ההתראה כדי להשבית ניפוי באגים ב-USB."</string>
     <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ניפוי הבאגים האלחוטי מחובר"</string>
     <string name="adbwifi_active_notification_message" msgid="930987922852867972">"יש להקיש כדי להשבית ניפוי באגים אלחוטי"</string>
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"הגדרת נעילת מסך"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"הגדרה של נעילת מסך"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"כדי להשתמש במרחב הפרטי יש להגדיר נעילת מסך במכשיר"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"כדי למחוק את המרחב הפרטי, צריך להגדיר נעילת מסך במכשיר הזה."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"האפליקציה לא זמינה"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא זמינה בשלב זה."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> לא זמינה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index bcea959..36d956c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1936,8 +1936,8 @@
     <string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"ダウンタイム"</string>
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"平日の夜"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"週末"</string>
-    <string name="zen_mode_default_events_name" msgid="2280682960128512257">"予定"</string>
-    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"睡眠中"</string>
+    <string name="zen_mode_default_events_name" msgid="2280682960128512257">"予定モード"</string>
+    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"おやすみモード"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> によって管理されています"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ON"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"OFF"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 29dfbba..595b9ba 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -642,7 +642,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Қолданбаға медиамазмұн жинағынан геодеректерді оқуға мүмкіндік береді."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Биометриканы пайдалану"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Биометриканы немесе экран құлпын пайдалану"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Бұл сіз екеніңізді растаңыз"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Cіз екеніңізді растаңыз"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Жалғастыру үшін биометрикаңызды пайдаланыңыз."</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Жалғастыру үшін биометриканы немесе экран құлпын пайдаланыңыз."</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалық жабдық жоқ"</string>
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Экран құлпын орнатыңыз"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Экран құлпын орнату"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Құпия кеңістігіңізді қолдану үшін осы құрылғыда экран құлпын орнатыңыз."</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Құпия кеңістікті жою үшін құрылғыға экран құлпын орнатыңыз."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Қолданба қолжетімді емес"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> қазір қолжетімді емес."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> қолжетімсіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index aeea5dc..696bd79 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"កំណត់​ការចាក់​សោអេក្រង់"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"កំណត់​ការចាក់​សោ​អេក្រង់"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ដើម្បីប្រើលំហឯកជនរបស់អ្នក សូមកំណត់ការចាក់សោអេក្រង់នៅលើឧបករណ៍នេះ"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"ដើម្បីលុបលំហឯកជនរបស់អ្នក សូមកំណត់ការចាក់សោអេក្រង់នៅលើឧបករណ៍នេះ"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"មិនអាច​ប្រើ​កម្មវិធី​នេះបានទេ"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"មិនអាច​ប្រើ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេល​នេះ​បានទេ​។"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"មិនអាចប្រើ <xliff:g id="ACTIVITY">%1$s</xliff:g> បានទេ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5ed6959..5f0e1f9 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"화면 잠금 설정"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"화면 잠금 설정"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"비공개 스페이스를 사용하려면 이 기기에 화면 잠금을 설정하세요"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"비공개 스페이스를 삭제하려면 이 기기에 화면 잠금을 설정하세요"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"앱을 사용할 수 없습니다"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> 사용할 수 없음"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 0c3a5e1..514b9cf 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Экран кулпусун коюп алыңыз"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Экран кулпусун коюу"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Жеке мейкиндикти колдонуу үчүн бул түзмөктүн экранын кулпулаңыз"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Жеке мейкиндикти өчүрүү үчүн бул түзмөктө экран кулпусун коюңуз"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Колдонмо учурда жеткиликсиз"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> учурда жеткиликсиз"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> жеткиликсиз"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 56945c2..e617a4c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekrano užrako nustatymas"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nustatykite ekrano užraktą"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Jei norite naudoti privačią erdvę, nustatykite ekrano užraktą šiame įrenginyje"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Jei norite ištrinti privačią erdvę, nustatykite ekrano užraktą šiame įrenginyje"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Programa nepasiekiama."</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu nepasiekiama."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"„<xliff:g id="ACTIVITY">%1$s</xliff:g>“ nepasiekiama"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 8b271d9..b04dd45 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Iestatiet ekrāna bloķēšanu"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Iestatīt ekrāna bloķēšanu"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Lai izmantotu privāto telpu, iestatiet ekrāna bloķēšanu."</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Lai dzēstu privāto telpu, iestatiet ekrāna bloķēšanu šajā ierīcē."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Lietotne nav pieejama"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik nav pieejama."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nav pieejams"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index a5ce587..b0d6351 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Поставете заклучување екран"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Поставете заклучување екран"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"За да користите „Приватен простор“, поставете заклучување екран на уредов"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"За да го избришете „Приватниот простор“, поставете заклучување екран на уредов"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Апликацијата не е достапна"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> не е достапна во моментов."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> е недостапна"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ef9d6c8..cc545e2 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"സ്‌ക്രീൻ ലോക്ക് സജ്ജീകരിക്കൂ"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"സ്‌ക്രീൻ ലോക്ക് സജ്ജീകരിക്കൂ"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"സ്വകാര്യ സ്പേസിന്, ഇതിൽ സ്ക്രീൻ ലോക്ക് സജ്ജീകരിക്കൂ"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"സ്വകാര്യ സ്പേസ് ഇല്ലാതാക്കാൻ, ഈ ഉപകരണത്തിൽ സ്ക്രീൻ ലോക്ക് സജ്ജീകരിക്കൂ"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ആപ്പ് ലഭ്യമല്ല"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ലഭ്യമല്ല"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 01a175a..87ce94b 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Дэлгэцийн түгжээ тохируулах"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Дэлгэцийн түгжээ тохируулах"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Хаалттай орон зайгаа ашиглах бол уг төхөөрөмжид дэлгэцийн түгжээ тохируулна уу"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Хаалттай орон зайг устгахын тулд энэ төхөөрөмж дээр дэлгэцийн түгжээ тохируулна уу"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Апп боломжгүй байна"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> яг одоо боломжгүй байна."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> боломжгүй байна"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index f9b70ff..03cabc3 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"स्क्रीन लॉक सेट करा"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"स्क्रीन लॉक सेट करा"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"तुमची खाजगी स्पेस वापरण्यास, या डिव्हाइसवर स्क्रीन लॉक सेट करा"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"खाजगी स्पेस हटवण्यासाठी, या डिव्हाइसवर स्क्रीन लॉक सेट करा"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ॲप उपलब्ध नाही"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता उपलब्ध नाही."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध नाही"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index b30d48d..83a1d56 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Tetapkan kunci skrin"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Tetapkan kunci skrin"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Tetapkan kunci skrin pada peranti untuk menggunakan ruang privasi"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Untuk memadamkan ruang peribadi, tetapkan kunci skrin pada peranti ini"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Apl tidak tersedia"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia sekarang."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> tidak tersedia"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index b78e2cb..e4e75c1 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ဖန်သားပြင်လော့ခ် သတ်မှတ်ပါ"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ဖန်သားပြင်လော့ခ် သတ်မှတ်ရန်"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"သင့်သီးသန့်နေရာသုံးရန် ဤစက်၌ ဖန်သားပြင်လော့ခ် သတ်မှတ်ပါ"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"သီးသန့်နေရာကို ဖျက်ရန်အတွက် ဤစက်တွင် ဖန်သားပြင်လော့ခ် သတ်မှတ်ပါ"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"အက်ပ်ကို မရနိုင်ပါ"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ယခု မရနိုင်ပါ။"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> မရနိုင်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 8002e90..6d24bdf 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Angi en skjermlås"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Angi skjermlås"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"For å bruke det private området, angi en skjermlås på enheten"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Før du kan slette det private området, må du angi en skjermlås på denne enheten"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgjengelig"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgjengelig for øyeblikket."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> er utilgjengelig"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 784cce4..133429b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1937,7 +1937,7 @@
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"हरेक हप्तादिनको राति"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"शनिवार"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"कार्यक्रम"</string>
-    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"निदाएका बेला"</string>
+    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"शयन"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले व्यवस्थापन गरेको"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"अन छ"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"अफ छ"</string>
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"स्क्रिन लक सेटअप गर्नुहोस्"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"स्क्रिन लक सेटअप गर्नुहोस्"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"निजी स्पेस प्रयोग गर्न यो डिभाइसमा स्क्रिन लक सेटअप गर्नुहोस्"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"निजी स्पेस मेटाउन यो डिभाइसमा स्क्रिन लक सेटअप गर्नुहोस्"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"एप उपलब्ध छैन"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> अहिले उपलब्ध छैन।"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> उपलब्ध छैन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 6180b1b..d2ec4ba 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Schermvergrendeling instellen"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Schermvergrendeling instellen"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Als je je privégedeelte wilt gebruiken, stel je een schermvergrendeling op dit apparaat in"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Als je het privégedeelte wilt verwijderen, stel je een schermvergrendeling op dit apparaat in"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"App is niet beschikbaar"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel niet beschikbaar."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> niet beschikbaar"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index e6e20fa..85a6aec 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ଏକ ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ଆପଣଙ୍କ ପ୍ରାଇଭେଟ ସ୍ପେସ ବ୍ୟବହାର କରିବାକୁ ଏହି ଡିଭାଇସରେ ଏକ ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଡିଲିଟ କରିବା ପାଇଁ ଏହି ଡିଭାଇସରେ ଏକ ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ନାହିଁ।"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ଉପଲବ୍ଧ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 8afb731..51d71ff 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਵਰਤਣ ਲਈ, ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਨੂੰ ਮਿਟਾਉਣ ਲਈ, ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 14320ffe..49c30c7 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ustaw blokadę ekranu"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ustaw blokadę ekranu"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Aby korzystać z przestrzeni prywatnej, ustaw na tym urządzeniu blokadę ekranu"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Aby usunąć przestrzeń prywatną, ustaw na tym urządzeniu blokadę ekranu"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – brak dostępu"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index e8e125c..ff07066 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Defina um bloqueio de tela"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Definir bloqueio de tela"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar o espaço privado, defina um bloqueio de tela neste dispositivo"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Para excluir o espaço privado, defina um bloqueio de tela neste dispositivo"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 910c5cf..12245b9 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1938,7 +1938,7 @@
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Dias da semana à noite"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
-    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"A dormir"</string>
+    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Gerido por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"Ativada"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Desativada"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index e8e125c..ff07066 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Defina um bloqueio de tela"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Definir bloqueio de tela"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para usar o espaço privado, defina um bloqueio de tela neste dispositivo"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Para excluir o espaço privado, defina um bloqueio de tela neste dispositivo"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> indisponível"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 29b9d17..6559764 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Настройте блокировку экрана"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Настроить блокировку экрана"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Чтобы использовать частное пространство, настройте блокировку экрана на этом устройстве."</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Чтобы удалить частное пространство, настройте блокировку экрана на этом устройстве."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Приложение недоступно"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" сейчас недоступно."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недоступно: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index acd6d43..be41512 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"තිර අගුලක් සකසන්න"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"තිර අගුල සකසන්න"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ඔබේ රහසිගත අවකාශය භාවිතා කිරීමට, මෙම උපාංගයේ තිර අගුලක් සකසන්න"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"පෞද්ගලික අවකාශය මැකීමට, මෙම උපාංගයෙහි තිර අගුලක් සකසන්න"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"යෙදුම ලබා ගත නොහැකිය"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> මේ දැන් ලබා ගත නොහැකිය."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> නොතිබේ"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 07b44f9..61d3229 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -644,7 +644,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Umožňuje aplikácii čítať polohy zo zbierky médií."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Použiť biometrické údaje"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Použiť biometrické údaje alebo zámku obrazovky"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Overenie, že ste to vy"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrďte, že ste to vy"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Ak chcete pokračovať, použite biometrický údaj"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Pokračujte použitím biometrických údajov alebo zámky obrazovky"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrický hardvér nie je k dispozícii"</string>
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Nastavte zámku obrazovky"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nastaviť zámku obrazovky"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ak chcete používať súkromný priestor, nastavte v tomto zariadení zámku obrazovky"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Ak chcete odstrániť súkromný priestor, nastavte v tomto zariadení zámku obrazovky"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikácia nie je dostupná"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> nie je teraz dostupná."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nie je k dispozícii"</string>
@@ -2411,9 +2410,9 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Rozloženie klávesnice je nastavené na jazyky <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> a <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Môžete to zmeniť klepnutím."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fyzické klávesnice sú nakonfigurované"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klávesnice si zobrazíte klepnutím"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Súkromné"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Súkromný"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string>
-    <string name="profile_label_work" msgid="3495359133038584618">"Pracovné"</string>
+    <string name="profile_label_work" msgid="3495359133038584618">"Pracovný"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"2. pracovný"</string>
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testovací"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index f3b0da4..ed8cf50 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Nastavitev zaklepanja zaslona"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Nastavite zaklepanje zaslona"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Če želite uporabljati zasebni prostor, v tej napravi nastavite zaklepanje zaslona"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Če želite izbrisati zasebni prostor, v tej napravi nastavite zaklepanje zaslona"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija ni na voljo"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno ni na voljo."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"»<xliff:g id="ACTIVITY">%1$s</xliff:g>« ni na voljo"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index badb90d..e24e310 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Cakto një kyçje ekrani"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Cakto kyçjen e ekranit"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Për të përdorur hapësirën private, cakto një kyçje ekrani në këtë pajisje"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Për të përdorur hapësirën private, cakto një kyçje ekrani në këtë pajisje"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacioni nuk ofrohet"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk ofrohet për momentin."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nuk ofrohet"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d38dd12..915486e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -2011,8 +2011,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Подесите откључавање екрана"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Подеси откључавање екрана"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Да бисте користили приватни простор, подесите откључавање екрана на овом уређају"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Да бисте избрисали приватан простор, подесите откључавање екрана на овом уређају"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Апликација није доступна"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> тренутно није доступна."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> – није доступно"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 9b7f3a1..0c5b6ac 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1937,7 +1937,7 @@
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Vardagskväll"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"I helgen"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Händelse"</string>
-    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"När jag sover"</string>
+    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Sover"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"Hanteras av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"På"</string>
     <string name="zen_mode_implicit_deactivated" msgid="8688441768371501750">"Av"</string>
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ställ in ett skärmlås"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ställ in skärmlås"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ställ in ett skärmlås för enheten om du vill använda ditt privata område."</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Ange ett skärmlås för enheten om du vill radera privat område"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Appen är inte tillgänglig"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> är inte tillgängligt just nu."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> är inte tillgänglig"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index e1123af..8d69834ea 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Weka mbinu ya kufunga skrini"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Weka mbinu ya kufunga skrini"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ili utumie sehemu ya faragha, weka mbinu ya kufunga skrini kwenye kifaa hiki"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Ili ufute sehemu ya faragha, weka mbinu ya kufunga skrini kwenye kifaa hiki"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Programu haipatikani"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> haipatikani hivi sasa."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> haipatikani"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index f167eb7..dfc6a79 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"திரைப் பூட்டை அமையுங்கள்"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"திரைப் பூட்டை அமை"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ரகசிய இடத்தைப் பயன்படுத்த, சாதனத்தில் திரைப் பூட்டை அமையுங்கள்"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"ரகசிய இடத்தை நீக்க, இந்தச் சாதனத்தில் திரைப் பூட்டை அமையுங்கள்"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"இந்த ஆப்ஸ் இப்போது கிடைப்பதில்லை"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இப்போது கிடைப்பதில்லை."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> இல்லை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 1f8a2f7..7073266 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -642,7 +642,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"మీ మీడియా సేకరణ నుండి లొకేషన్లను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"బయోమెట్రిక్స్‌ను ఉపయోగించండి"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"బయోమెట్రిక్స్‌ను లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"ఇది మీరేనని వెరిఫై చేసుకోండి"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"ఈ చర్య చేస్తోంది మీరేనని వెరిఫై చేయండి"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"కొనసాగించడానికి, మీ బయోమెట్రిక్‌ను ఉపయోగించండి"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"కొనసాగించడానికి మీ బయోమెట్రిక్ లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"బయోమెట్రిక్ హార్డ్‌వేర్‌ అందుబాటులో లేదు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index cc716ea..51bb90f 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ตั้งล็อกหน้าจอ"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ตั้งล็อกหน้าจอ"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"หากต้องการใช้พื้นที่ส่วนตัว ให้ตั้งการล็อกหน้าจอในอุปกรณ์นี้"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"หากต้องการลบพื้นที่ส่วนตัว ให้ตั้งการล็อกหน้าจอในอุปกรณ์นี้"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"แอปไม่พร้อมใช้งาน"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่พร้อมใช้งานในขณะนี้"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> ไม่พร้อมใช้งาน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 8939120..e76dacd 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Magtakda ng lock ng screen"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Itakda ang lock ng screen"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Para gamitin ang iyong pribadong space, magtakda ng lock ng screen sa device na ito."</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Para mag-delete ng pribadong space, magtakda ng lock ng screen sa device na ito"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Hindi available ang app"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Hindi available sa ngayon ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"Hindi available ang <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
@@ -2440,7 +2439,7 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"Hindi na makilala ang iyong face model. I-set up ulit ang Pag-unlock Gamit ang Mukha."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"I-set up"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Huwag muna"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm para kay/sa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm para kay <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Magpalit ng user"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"I-mute"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"I-tap para i-mute ang tunog"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 85fea5c..ad6fc0d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -642,7 +642,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Uygulamanın medya koleksiyonunuzdaki konumları okumasına izin verir."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Biyometri kullan"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Biyometri veya ekran kilidi kullan"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Siz olduğunuzu doğrulayın"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Kimliğinizi doğrulayın"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Devam etmek için biyometri kullanın"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Devam etmek için biyometrik kimlik bilginizi veya ekran kilidinizi kullanın"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biyometrik donanım kullanılamıyor"</string>
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekran kilidi ayarlayın"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ekran kilidi ayarla"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Özel alanı kullanmak için cihazda ekran kilidi ayarlayın"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Gizli alanı silmek için bu cihazda ekran kilidi ayarlayın."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Uygulama kullanılamıyor"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması şu anda kullanılamıyor."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> kullanılamıyor"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 614925f..a63f3fb 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -2012,8 +2012,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Налаштуйте блокування екрана"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Налаштувати блокування екрана"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Для доступу до приватного простору налаштуйте блокування екрана"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Щоб видалити приватний простір, налаштуйте блокування екрана на цьому пристрої"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Додаток недоступний"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> зараз недоступний."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"Недоступно: <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 660068d..52d9e3b 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -2439,7 +2439,7 @@
     <string name="face_dangling_notification_msg" msgid="746235263598985384">"آپ کے چہرے کا ماڈل مزید پہچانا نہیں جا سکتا۔ فیس اَن لاک کو دوبارہ سیٹ اپ کریں۔"</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"سیٹ اپ کریں"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ابھی نہیں"</string>
-    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"<xliff:g id="USER_NAME">%s</xliff:g> کیلئے الارم"</string>
+    <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"‫<xliff:g id="USER_NAME">%s</xliff:g> کیلئے الارم"</string>
     <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"صارف سوئچ کریں"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"خاموش کریں"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"آواز کو خاموش کرنے کے لیے تھپتھپائیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 7bd6175..f62074e 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Ekran qulfini sozlash"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Ekran qulfini sozlash"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Maxfiy makon ishlatish uchun bu qurilma ekran qulfini sozlang"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Maxfiy makon ishlatish uchun bu qurilma ekran qulfini sozlang"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Ilova ishlamayapti"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"Ayni vaqtda <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi ishlamayapti."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> kanali ish faoliyatida emas"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index f084c37..3b08a22 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Đặt phương thức khoá màn hình"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Đặt phương thức khoá màn hình"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Để dùng không gian riêng tư, hãy thiết lập một phương thức khoá màn hình trên thiết bị này"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Để xoá không gian riêng tư, hãy đặt một phương thức khoá màn hình trên thiết bị này"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Ứng dụng này không dùng được"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện không dùng được."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"Không hỗ trợ <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index a1c36ab..95753eb 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"设置一种屏锁方式"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"设置屏锁方式"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"若要使用私密空间,请在此设备上设置屏锁方式"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"若要删除私密空间,请在此设备上设置屏幕锁定"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"应用无法使用"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前无法使用。"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g>不可用"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 6a5fea7..df04ec8 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"設定螢幕鎖定"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"設定螢幕鎖定"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"如要使用私人空間,請在此裝置上設定螢幕鎖定功能"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"如要刪除私人空間,請在此裝置上設定螢幕鎖定功能"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"無法使用應用程式"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"目前無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"無法使用「<xliff:g id="ACTIVITY">%1$s</xliff:g>」"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index bb64ab5..f0351ce 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"設定螢幕鎖定功能"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"設定螢幕鎖定功能"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"如要使用私人空間,請在這部裝置設定螢幕鎖定功能"</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"如要刪除私人空間,請在這部裝置上設定螢幕鎖定功能"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"應用程式無法使用"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前無法使用。"</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"無法存取「<xliff:g id="ACTIVITY">%1$s</xliff:g>」"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 0524c09..f051a45 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -2010,8 +2010,7 @@
     <string name="set_up_screen_lock_title" msgid="8346083801616474030">"Setha ukukhiya isikrini"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Setha ukukhiya isikrini"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Ukuze usebenzise isikhala esigodliwe, setha ukukhiya kwesikrini kule divayisi."</string>
-    <!-- no translation found for private_space_set_up_screen_lock_for_reset (7817091386408432097) -->
-    <skip />
+    <string name="private_space_set_up_screen_lock_for_reset" msgid="7817091386408432097">"Ukuze usule Indawo Engasese, setha ukukhiya kwesikrini kule divayisi."</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"Uhlelo lokusebenza alutholakali"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayitholakali khona manje."</string>
     <string name="app_streaming_blocked_title" msgid="6090945835898766139">"okungatholakali <xliff:g id="ACTIVITY">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 421b7d2..92c3906 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4380,7 +4380,7 @@
              modes dimensions {@link config_minPercentageMultiWindowSupportWidth} the device
              supports to determine if the activity can be shown in multi windowing modes.
     -->
-    <integer name="config_respectsActivityMinWidthHeightMultiWindow">0</integer>
+    <integer name="config_respectsActivityMinWidthHeightMultiWindow">-1</integer>
 
     <!-- This value is only used when the device checks activity min height to determine if it
          can be shown in multi windowing modes.
@@ -4457,6 +4457,11 @@
     <!-- Bytes that the PinnerService will pin for WebView -->
     <integer name="config_pinnerWebviewPinBytes">0</integer>
 
+    <!-- Maximum memory that PinnerService will pin for apps expressed
+         as a percentage of total device memory [0,100].
+         Example: 10, means 10% of total memory will be the maximum pinned memory -->
+    <integer name="config_pinnerMaxPinnedMemoryPercentage">10</integer>
+
     <!-- Number of days preloaded file cache should be preserved on a device before it can be
          deleted -->
     <integer name="config_keepPreloadsMinDays">7</integer>
@@ -7136,4 +7141,8 @@
     <!-- Whether to enable scaling and fading animation to scrollviews while scrolling.
          P.S this is a change only intended for wear devices. -->
     <bool name="config_enableViewGroupScalingFading">false</bool>
+    <!-- Action for opening identity check settings page [CHAR LIMIT=NONE] [DO NOT TRANSLATE] -->
+    <string name="identity_check_settings_action"></string>
+    <!-- Package for opening identity check settings page [CHAR LIMIT=NONE] [DO NOT TRANSLATE] -->
+    <string name="identity_check_settings_package_name">com\u002eandroid\u002esettings</string>
 </resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 69437b4..9854030 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -287,6 +287,11 @@
     <string name="config_satellite_demo_mode_sos_intent_action" translatable="false"></string>
     <java-symbol type="string" name="config_satellite_demo_mode_sos_intent_action" />
 
+    <!-- The action of the intent that hidden menu sends to the app to launch esp loopback test mode
+     for sos emergency messaging via satellite. -->
+    <string name="config_satellite_test_with_esp_replies_intent_action" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_test_with_esp_replies_intent_action" />
+
     <!-- Whether outgoing satellite datagrams should be sent to modem in demo mode. When satellite
          is enabled for demo mode, if this config is enabled, outgoing datagrams will be sent to
          modem; otherwise, success results will be returned. If demo mode is disabled, outgoing
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f397ef2..6683dc0 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -808,6 +808,12 @@
          This is bigger than displayed because listeners can use it for other displays
          e.g. wearables. -->
     <dimen name="notification_person_icon_max_size">144dp</dimen>
+    <!-- The size of the progress bar icon -->
+    <dimen name="notification_progress_icon_size">20dp</dimen>
+    <!-- The size of the progress tracker width -->
+    <dimen name="notification_progress_tracker_width">40dp</dimen>
+    <!-- The size of the progress tracker height -->
+    <dimen name="notification_progress_tracker_height">20dp</dimen>
 
     <!-- The maximum size of the small notification icon on low memory devices. -->
     <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0396659..5f40a6c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3467,6 +3467,7 @@
   <java-symbol type="integer" name="config_pinnerHomePinBytes" />
   <java-symbol type="bool" name="config_pinnerAssistantApp" />
   <java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
+  <java-symbol type="integer" name="config_pinnerMaxPinnedMemoryPercentage" />
 
   <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
 
@@ -3854,6 +3855,9 @@
   <java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
   <java-symbol type="dimen" name="notification_person_icon_max_size" />
+  <java-symbol type="dimen" name="notification_progress_icon_size" />
+  <java-symbol type="dimen" name="notification_progress_tracker_width" />
+  <java-symbol type="dimen" name="notification_progress_tracker_height" />
 
   <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
   <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
@@ -5611,4 +5615,8 @@
   <java-symbol type="string" name="fingerprint_loe_notification_msg" />
 
   <java-symbol type="bool" name="config_enableViewGroupScalingFading"/>
+
+  <!-- Identity check strings -->
+  <java-symbol type="string" name="identity_check_settings_action" />
+  <java-symbol type="string" name="identity_check_settings_package_name" />
 </resources>
diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
index d34c91e..e81cdee9 100644
--- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
+++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
@@ -22,7 +22,10 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 
+import static org.junit.Assume.assumeNotNull;
+
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -47,6 +50,9 @@
     public void setUp() {
         mContext = getInstrumentation().getContext();
         mGameManager = mContext.getSystemService(GameManager.class);
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            assumeNotNull(mGameManager);
+        }
         mPackageName = mContext.getPackageName();
 
         // Reset the Game Mode for the test app, since it persists across invocations.
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index d98836f..9821d43 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -25,6 +25,7 @@
         "BinderProxyCountingTestApp/src/**/*.java",
         "BinderProxyCountingTestService/src/**/*.java",
         "BinderDeathRecipientHelperApp/src/**/*.java",
+        "AppThatCallsBinderMethods/src/**/*.kt",
     ],
     visibility: ["//visibility:private"],
 }
@@ -104,6 +105,7 @@
         "mockito-target-extended-minus-junit4",
         "TestParameterInjector",
         "android.content.res.flags-aconfig-java",
+        "android.security.flags-aconfig-java",
     ],
 
     libs: [
@@ -143,6 +145,7 @@
         ":BinderProxyCountingTestApp",
         ":BinderProxyCountingTestService",
         ":AppThatUsesAppOps",
+        ":AppThatCallsBinderMethods",
     ],
 }
 
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index b1f1e2c..05ab783 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -26,6 +26,7 @@
         <option name="test-file-name" value="BinderProxyCountingTestApp.apk" />
         <option name="test-file-name" value="BinderProxyCountingTestService.apk" />
         <option name="test-file-name" value="AppThatUsesAppOps.apk" />
+        <option name="test-file-name" value="AppThatCallsBinderMethods.apk" />
     </target_preparer>
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/core/tests/coretests/AppThatCallsBinderMethods/Android.bp b/core/tests/coretests/AppThatCallsBinderMethods/Android.bp
new file mode 100644
index 0000000..dcc0d4f
--- /dev/null
+++ b/core/tests/coretests/AppThatCallsBinderMethods/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+android_test_helper_app {
+    name: "AppThatCallsBinderMethods",
+    srcs: ["src/**/*.kt"],
+    platform_apis: true,
+    static_libs: ["coretests-aidl"],
+}
diff --git a/core/tests/coretests/AppThatCallsBinderMethods/AndroidManifest.xml b/core/tests/coretests/AppThatCallsBinderMethods/AndroidManifest.xml
new file mode 100644
index 0000000..b2f6d78
--- /dev/null
+++ b/core/tests/coretests/AppThatCallsBinderMethods/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.methodcallerhelperapp">
+    <application>
+        <receiver android:name="com.android.frameworks.coretests.methodcallerhelperapp.CallMethodsReceiver"
+                  android:exported="true"/>
+    </application>
+
+</manifest>
diff --git a/core/tests/coretests/AppThatCallsBinderMethods/src/com/android/frameworks/coretests/methodcallerhelperapp/CallMethodsReceiver.kt b/core/tests/coretests/AppThatCallsBinderMethods/src/com/android/frameworks/coretests/methodcallerhelperapp/CallMethodsReceiver.kt
new file mode 100644
index 0000000..638cc3b
--- /dev/null
+++ b/core/tests/coretests/AppThatCallsBinderMethods/src/com/android/frameworks/coretests/methodcallerhelperapp/CallMethodsReceiver.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.frameworks.coretests.methodcallerhelperapp
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+
+import com.android.frameworks.coretests.aidl.ITestInterface
+
+/**
+ * Receiver used to call methods when a binder is received
+ * {@link android.os.BinderUncaughtExceptionHandlerTest}.
+ */
+class CallMethodsReceiver : BroadcastReceiver() {
+    private val TAG = "CallMethodsReceiver"
+
+    override fun onReceive(context: Context, intent: Intent) {
+        try {
+            when (intent.getAction()) {
+                ACTION_CALL_METHOD -> intent.getExtras()!!.let {
+                    Log.i(TAG, "Received ACTION_CALL_METHOD with extras: $it")
+                    val iface = it.getBinder(EXTRA_BINDER)!!.let(ITestInterface.Stub::asInterface)!!
+                    val name = it.getString(EXTRA_METHOD_NAME)!!
+                    try {
+                        when (name) {
+                            "foo" -> iface.foo(5)
+                            "onewayFoo" -> iface.onewayFoo(5)
+                            "bar" -> iface.bar(5)
+                            else -> Log.e(TAG, "Unknown method name")
+                        }
+                    } catch (e: Exception) {
+                        // Exceptions expected
+                    }
+                }
+                else -> Log.e(TAG, "Unknown action " + intent.getAction())
+            }
+        } catch (e: Exception) {
+            Log.e(TAG, "Exception: ", e)
+        }
+    }
+}
diff --git a/core/tests/coretests/AppThatCallsBinderMethods/src/com/android/frameworks/coretests/methodcallerhelperapp/Constants.kt b/core/tests/coretests/AppThatCallsBinderMethods/src/com/android/frameworks/coretests/methodcallerhelperapp/Constants.kt
new file mode 100644
index 0000000..37c6268
--- /dev/null
+++ b/core/tests/coretests/AppThatCallsBinderMethods/src/com/android/frameworks/coretests/methodcallerhelperapp/Constants.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.frameworks.coretests.methodcallerhelperapp
+
+const val PACKAGE_NAME = "com.android.frameworks.coretests.methodcallerhelperapp"
+const val RECEIVER_NAME = "CallMethodsReceiver"
+const val ACTION_CALL_METHOD = PACKAGE_NAME + ".ACTION_CALL_METHOD"
+const val EXTRA_METHOD_NAME = PACKAGE_NAME + ".EXTRA_METHOD_NAME"
+const val EXTRA_BINDER = PACKAGE_NAME + ".EXTRA_BINDER"
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestInterface.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestInterface.aidl
new file mode 100644
index 0000000..ffcf178
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ITestInterface.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.frameworks.coretests.aidl;
+
+/**
+ * Just an interface with a oneway, void and non-oneway method.
+ */
+interface ITestInterface {
+    // Method order matters, since we verify transaction codes
+    int foo(int a);
+    oneway void onewayFoo(int a);
+    void bar(int a);
+}
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 0837b45..edcea24 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -37,6 +37,7 @@
 import static android.app.Notification.EXTRA_PICTURE_ICON;
 import static android.app.Notification.EXTRA_SUMMARY_TEXT;
 import static android.app.Notification.EXTRA_TITLE;
+import static android.app.Notification.FLAG_CAN_COLORIZE;
 import static android.app.Notification.GROUP_ALERT_CHILDREN;
 import static android.app.Notification.GROUP_ALERT_SUMMARY;
 import static android.app.Notification.GROUP_KEY_SILENT;
@@ -116,6 +117,7 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -126,6 +128,8 @@
 
     private Context mContext;
 
+    private RemoteViews mRemoteViews;
+
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -133,23 +137,25 @@
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
+        mRemoteViews = new RemoteViews(
+                mContext.getPackageName(), R.layout.notification_template_header);
     }
 
     @Test
     public void testColorizedByPermission() {
         Notification n = new Notification.Builder(mContext, "test")
-                .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
                 .setColorized(true).setColor(Color.WHITE)
                 .build();
         assertTrue(n.isColorized());
 
         n = new Notification.Builder(mContext, "test")
-                .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
                 .build();
         assertFalse(n.isColorized());
 
         n = new Notification.Builder(mContext, "test")
-                .setFlag(Notification.FLAG_CAN_COLORIZE, false)
+                .setFlag(FLAG_CAN_COLORIZE, false)
                 .setColorized(true).setColor(Color.WHITE)
                 .build();
         assertFalse(n.isColorized());
@@ -215,6 +221,275 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasTitle_noStyle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setContentTitle("TITLE")
+                .build();
+        assertThat(n.hasTitle()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasTitle_bigText() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .build();
+        assertThat(n.hasTitle()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasTitle_noTitle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setContentText("text not title")
+                .build();
+        assertThat(n.hasTitle()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testContainsCustomViews_none() {
+        Notification np = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .build();
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setPublicVersion(np)
+                .build();
+        assertThat(n.containsCustomViews()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testContainsCustomViews_content() {
+        Notification np = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .build();
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setCustomContentView(mRemoteViews)
+                .setPublicVersion(np)
+                .build();
+        assertThat(n.containsCustomViews()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testContainsCustomViews_big() {
+        Notification np = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .build();
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setCustomBigContentView(mRemoteViews)
+                .setPublicVersion(np)
+                .build();
+        assertThat(n.containsCustomViews()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testContainsCustomViews_headsUp() {
+        Notification np = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .build();
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setCustomHeadsUpContentView(mRemoteViews)
+                .setPublicVersion(np)
+                .build();
+        assertThat(n.containsCustomViews()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testContainsCustomViews_content_public() {
+        Notification np = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("public")
+                .setCustomContentView(mRemoteViews)
+                .build();
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setPublicVersion(np)
+                .build();
+        assertThat(n.containsCustomViews()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testContainsCustomViews_big_public() {
+        Notification np = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setCustomBigContentView(mRemoteViews)
+                .build();
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setPublicVersion(np)
+                .build();
+        assertThat(n.containsCustomViews()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testContainsCustomViews_headsUp_public() {
+        Notification np = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setCustomHeadsUpContentView(mRemoteViews)
+                .build();
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .setPublicVersion(np)
+                .build();
+        assertThat(n.containsCustomViews()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_noStyle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setContentText("test")
+                .build();
+        assertThat(n.hasPromotableStyle()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_bigPicture() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigPictureStyle())
+                .build();
+        assertThat(n.hasPromotableStyle()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_bigText() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle())
+                .build();
+        assertThat(n.hasPromotableStyle()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_no_messagingStyle() {
+        Notification.MessagingStyle style = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle("test conversation title");
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(style)
+                .build();
+        assertThat(n.hasPromotableStyle()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_no_mediaStyle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.MediaStyle())
+                .build();
+        assertThat(n.hasPromotableStyle()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_no_inboxStyle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.InboxStyle())
+                .build();
+        assertThat(n.hasPromotableStyle()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableStyle_callText() {
+        PendingIntent answerIntent = createPendingIntent("answer");
+        PendingIntent declineIntent = createPendingIntent("decline");
+        Notification.CallStyle style = Notification.CallStyle.forIncomingCall(
+                new Person.Builder().setName("A Caller").build(),
+                declineIntent,
+                answerIntent
+        );
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(style)
+                .build();
+        assertThat(n.hasPromotableStyle()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableCharacteristics() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
+                .build();
+        assertThat(n.hasPromotableCharacteristics()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableCharacteristics_wrongStyle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.InboxStyle())
+                .setContentTitle("TITLE")
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
+                .build();
+        assertThat(n.hasPromotableCharacteristics()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableCharacteristics_notColorized() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .build();
+        assertThat(n.hasPromotableCharacteristics()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
+    public void testHasPromotableCharacteristics_noTitle() {
+        Notification n = new Notification.Builder(mContext, "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle())
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
+                .build();
+        assertThat(n.hasPromotableCharacteristics()).isFalse();
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
     public void testGetShortCriticalText_noneSet() {
         Notification n = new Notification.Builder(mContext, "test")
@@ -1839,6 +2114,300 @@
         assertThat(n.getWhen()).isEqualTo(9);
     }
 
+    @Test
+    public void getNotificationStyleClass_forPlatformClassName_returnsPlatformClass() {
+        final List<Class<? extends Notification.Style>> platformStyleClasses = List.of(
+                Notification.BigTextStyle.class, Notification.BigPictureStyle.class,
+                Notification.MessagingStyle.class, Notification.CallStyle.class,
+                Notification.InboxStyle.class, Notification.MediaStyle.class,
+                Notification.DecoratedCustomViewStyle.class,
+                Notification.DecoratedMediaCustomViewStyle.class
+        );
+
+        for (Class<? extends Notification.Style> platformStyleClass : platformStyleClasses) {
+            assertThat(Notification.getNotificationStyleClass(platformStyleClass.getName()))
+                    .isEqualTo(platformStyleClass);
+        }
+    }
+
+    @Test
+    public void getNotificationStyleClass_forNotPlatformClassName_returnsNull() {
+        assertThat(Notification.getNotificationStyleClass(NotAPlatformStyle.class.getName()))
+                .isNull();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_richOngoingEnabled_platformClass() {
+        assertThat(
+                Notification.getNotificationStyleClass(Notification.ProgressStyle.class.getName()))
+                .isEqualTo(Notification.ProgressStyle.class);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_richOngoingDisabled_notPlatformClass() {
+        assertThat(
+                Notification.getNotificationStyleClass(Notification.ProgressStyle.class.getName()))
+                .isNull();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onSegmentChange_visiblyDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(100))
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(50)
+                                .setColor(Color.RED)));
+
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(100))
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(50)
+                                .setColor(Color.BLUE)));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void indeterminateProgressStyle_onSegmentChange_visiblyNotDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true)
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(100))
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(50)
+                                .setColor(Color.RED)));
+
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true)
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(100))
+                        .addProgressSegment(new Notification.ProgressStyle.Segment(50)
+                                .setColor(Color.BLUE)));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onStartIconChange_visiblyDifferent() {
+        final Icon icon1 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
+
+        final Icon icon2 = Icon.createWithBitmap(
+                Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888));
+
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressStartIcon(icon1));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressStartIcon(icon2));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onEndIconChange_visiblyDifferent() {
+        final Icon icon1 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
+
+        final Icon icon2 = Icon.createWithBitmap(
+                Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888));
+
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressEndIcon(icon1));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressEndIcon(icon2));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onProgressChange_visiblyDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgress(20));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgress(21));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void indeterminateProgressStyle_onProgressChange_visiblyNotDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .setProgressIndeterminate(true).setProgress(20));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .setProgressIndeterminate(true).setProgress(21));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onIsStyledByProgressChange_visiblyDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setStyledByProgress(true));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setStyledByProgress(false));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void indeterminateProgressStyle_onIsStyledByProgressChange_visiblyNotDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .setProgressIndeterminate(true).setStyledByProgress(true));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .setProgressIndeterminate(true).setStyledByProgress(false));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onProgressStepChange_visiblyDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .addProgressStep(new Notification.ProgressStyle.Step(10)));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .addProgressStep(new Notification.ProgressStyle.Step(12)));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void indeterminateProgressStyle_onProgressStepChange_visiblyNotDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true)
+                        .addProgressStep(new Notification.ProgressStyle.Step(10)));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true)
+                        .addProgressStep(new Notification.ProgressStyle.Step(12)));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onTrackerIconChange_visiblyDifferent() {
+        final Icon icon1 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
+
+        final Icon icon2 = Icon.createWithBitmap(
+                Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888));
+
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressTrackerIcon(icon1));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressTrackerIcon(icon2));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void indeterminateProgressStyle_onTrackerIconChange_visiblyNotDifferent() {
+        final Icon icon1 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96));
+
+        final Icon icon2 = Icon.createWithBitmap(
+                Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888));
+
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true)
+                        .setProgressTrackerIcon(icon1));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle()
+                        .setProgressIndeterminate(true).setProgressTrackerIcon(icon2));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_onIndeterminateChange_visiblyDifferent() {
+        final Notification.Builder nProgress1 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(true));
+        final Notification.Builder nProgress2 = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.ProgressStyle().setProgressIndeterminate(false));
+
+        assertThat(Notification.areStyledNotificationsVisiblyDifferent(nProgress1, nProgress2))
+                .isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_getProgressMax_default100() {
+        final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+        assertThat(progressStyle.getProgressMax()).isEqualTo(100);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_getProgressMax_nooSegments_returnsDefault() {
+        final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+        progressStyle.setProgressSegments(Collections.emptyList());
+        assertThat(progressStyle.getProgressMax()).isEqualTo(100);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_getProgressMax_returnsSumOfSegmentLength() {
+        final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+        progressStyle
+                .addProgressSegment(new Notification.ProgressStyle.Segment(10))
+                .addProgressSegment(new Notification.ProgressStyle.Segment(20));
+
+        assertThat(progressStyle.getProgressMax()).isEqualTo(30);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_getProgressMax_onSegmentOverflow_returnsDefault() {
+        final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+        progressStyle
+                .addProgressSegment(new Notification.ProgressStyle.Segment(Integer.MAX_VALUE))
+                .addProgressSegment(new Notification.ProgressStyle.Segment(10));
+
+        assertThat(progressStyle.getProgressMax()).isEqualTo(100);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_indeterminate_defaultValueFalse() {
+        final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
+
+        assertThat(progressStyle1.isProgressIndeterminate()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_styledByProgress_defaultValueTrue() {
+        final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
+
+        assertThat(progressStyle1.isStyledByProgress()).isTrue();
+    }
     private void assertValid(Notification.Colors c) {
         // Assert that all colors are populated
         assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID);
@@ -1939,4 +2508,11 @@
                 new Intent(action).setPackage(mContext.getPackageName()),
                 PendingIntent.FLAG_MUTABLE);
     }
+
+    private static class NotAPlatformStyle extends Notification.Style {
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Notification.Style other) {
+            return false;
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/content/IntentTest.java b/core/tests/coretests/src/android/content/IntentTest.java
new file mode 100644
index 0000000..d169ce3
--- /dev/null
+++ b/core/tests/coretests/src/android/content/IntentTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.content;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.security.Flags;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ *  Build/Install/Run:
+ *   atest FrameworksCoreTests:IntentTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentTest {
+    private static final String TEST_ACTION = "android.content.IntentTest_test";
+    private static final String TEST_EXTRA_NAME = "testExtraName";
+    private static final Uri TEST_URI = Uri.parse("content://com.example/people");
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT)
+    public void testReadFromParcelWithExtraIntentKeys() {
+        Intent intent = new Intent("TEST_ACTION");
+        intent.putExtra(TEST_EXTRA_NAME, new Intent(TEST_ACTION));
+        intent.putExtra(TEST_EXTRA_NAME + "2", 1);
+
+        intent.collectExtraIntentKeys();
+        final Parcel parcel = Parcel.obtain();
+        intent.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        final Intent target = new Intent();
+        target.readFromParcel(parcel);
+
+        assertEquals(intent.getAction(), target.getAction());
+        assertEquals(intent.getExtraIntentKeys(), target.getExtraIntentKeys());
+        assertThat(intent.getExtraIntentKeys()).hasSize(1);
+    }
+
+    @Test
+    public void testCreatorTokenInfo() {
+        Intent intent = new Intent(TEST_ACTION);
+        IBinder creatorToken = new Binder();
+
+        intent.setCreatorToken(creatorToken);
+        assertThat(intent.getCreatorToken()).isEqualTo(creatorToken);
+
+        intent.removeCreatorTokenInfo();
+        assertThat(intent.getCreatorToken()).isNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT)
+    public void testCollectExtraIntentKeys() {
+        Intent intent = new Intent(TEST_ACTION);
+        Intent extraIntent = new Intent(TEST_ACTION, TEST_URI);
+        intent.putExtra(TEST_EXTRA_NAME, extraIntent);
+
+        intent.collectExtraIntentKeys();
+
+        assertThat(intent.getExtraIntentKeys()).hasSize(1);
+        assertThat(intent.getExtraIntentKeys()).contains(TEST_EXTRA_NAME);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/os/BinderUncaughtExceptionHandlerTest.kt b/core/tests/coretests/src/android/os/BinderUncaughtExceptionHandlerTest.kt
new file mode 100644
index 0000000..791c209
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderUncaughtExceptionHandlerTest.kt
@@ -0,0 +1,247 @@
+/*
+ * 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.os
+
+import android.content.Intent
+import android.platform.test.annotations.DisabledOnRavenwood
+import android.platform.test.annotations.Presubmit
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+
+import com.android.frameworks.coretests.aidl.ITestInterface
+import com.android.frameworks.coretests.methodcallerhelperapp.*
+
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.mockito.ArgumentMatcher
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.intThat
+import org.mockito.Mockito.after
+import org.mockito.Mockito.doThrow
+import org.mockito.Mockito.never
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.quality.Strictness.STRICT_STUBS
+
+private const val TIMEOUT_DURATION_MS = 2000L
+private const val FALSE_NEG_DURATION_MS = 500L
+private const val FLAG_ONEWAY = 1
+// From ITestInterface.Stub class, these values are package private
+private const val TRANSACTION_foo = 1
+private const val TRANSACTION_onewayFoo = 2
+private const val TRANSACTION_bar = 3
+
+/** Tests functionality of {@link android.os.Binder.onUnhandledException}. */
+@DisabledOnRavenwood(reason = "multi-app")
+@Presubmit
+@RunWith(AndroidJUnit4::class)
+class BinderUncaughtExceptionHandlerTest {
+
+    val mContext = InstrumentationRegistry.getInstrumentation().getTargetContext()
+
+    @Rule @JvmField val rule = MockitoJUnit.rule().strictness(STRICT_STUBS)
+
+    @Spy var mInterfaceImpl: ITestImpl = ITestImpl()
+
+    // This subclass is needed for visibility issues (via protected), since the method we are
+    // verifying lives on the boot classpath, it is not enough to be in the same package.
+    open class ITestImpl : ITestInterface.Stub() {
+        override fun onUnhandledException(code: Int, flags: Int, e: Exception?) =
+            onUnhandledExceptionVisible(code, flags, e)
+
+        public open fun onUnhandledExceptionVisible(code: Int, flags: Int, e: Exception?) {}
+
+        @Throws(RemoteException::class)
+        override open fun foo(x: Int): Int = throw UnsupportedOperationException()
+
+        @Throws(RemoteException::class)
+        override open fun onewayFoo(x: Int): Unit = throw UnsupportedOperationException()
+
+        @Throws(RemoteException::class)
+        override open fun bar(x: Int): Unit = throw UnsupportedOperationException()
+    }
+
+    class OnewayMatcher(private val isOneway: Boolean) : ArgumentMatcher<Int> {
+        override fun matches(argument: Int?) =
+            (argument!! and FLAG_ONEWAY) == if (isOneway) 1 else 0
+
+        override fun toString() = "Expected oneway: $isOneway"
+    }
+
+    @Test
+    fun testRegularMethod_ifThrowsRuntimeException_HandlerCalled() {
+        val myException = RuntimeException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).foo(anyInt())
+
+        dispatchActionCall("foo")
+
+        verify(mInterfaceImpl, timeout(TIMEOUT_DURATION_MS))
+            .onUnhandledExceptionVisible(
+                /* transactionCode = */ eq(TRANSACTION_foo),
+                /* flags= */ intThat(OnewayMatcher(false)),
+                /* exception= */ eq(myException),
+            )
+        // No unexpected calls
+        verify(mInterfaceImpl).onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    @Test
+    fun testRegularMethod_ifThrowsRemoteException_HandlerCalled() {
+        val myException = RemoteException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).foo(anyInt())
+
+        dispatchActionCall("foo")
+
+        verify(mInterfaceImpl, timeout(TIMEOUT_DURATION_MS))
+            .onUnhandledExceptionVisible(
+                /* transactionCode = */ eq(TRANSACTION_foo),
+                /* flags= */ intThat(OnewayMatcher(false)),
+                /* exception= */ eq(myException),
+            )
+        // No unexpected calls
+        verify(mInterfaceImpl).onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    @Test
+    fun testRegularMethod_ifThrowsSecurityException_HandlerNotCalled() {
+        val myException = SecurityException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).foo(anyInt())
+
+        dispatchActionCall("foo")
+
+        // No unexpected calls
+        verify(mInterfaceImpl, after(FALSE_NEG_DURATION_MS).never())
+            .onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    @Test
+    fun testVoidMethod_ifThrowsRuntimeException_HandlerCalled() {
+        val myException = RuntimeException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).bar(anyInt())
+
+        dispatchActionCall("bar")
+
+        verify(mInterfaceImpl, timeout(TIMEOUT_DURATION_MS))
+            .onUnhandledExceptionVisible(
+                /* transactionCode = */ eq(TRANSACTION_bar),
+                /* flags= */ intThat(OnewayMatcher(false)),
+                /* exception= */ eq(myException),
+            )
+        // No unexpected calls
+        verify(mInterfaceImpl).onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    @Test
+    fun testVoidMethod_ifThrowsRemoteException_HandlerCalled() {
+        val myException = RemoteException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).bar(anyInt())
+
+        dispatchActionCall("bar")
+
+        verify(mInterfaceImpl, timeout(TIMEOUT_DURATION_MS))
+            .onUnhandledExceptionVisible(
+                /* transactionCode = */ eq(TRANSACTION_bar),
+                /* flags= */ intThat(OnewayMatcher(false)),
+                /* exception= */ eq(myException),
+            )
+        // No unexpected calls
+        verify(mInterfaceImpl).onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    @Test
+    fun testVoidMethod_ifThrowsSecurityException_HandlerNotCalled() {
+        val myException = SecurityException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).bar(anyInt())
+
+        dispatchActionCall("bar")
+
+        // No unexpected calls
+        verify(mInterfaceImpl, after(FALSE_NEG_DURATION_MS).never())
+            .onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    @Test
+    fun testOnewayMethod_ifThrowsRuntimeException_HandlerCalled() {
+        val myException = RuntimeException("Test exception")
+        doThrow(myException).doNothing().`when`(mInterfaceImpl).onewayFoo(anyInt())
+
+        dispatchActionCall("onewayFoo")
+
+        verify(mInterfaceImpl, timeout(TIMEOUT_DURATION_MS))
+            .onUnhandledExceptionVisible(
+                /* transactionCode = */ eq(TRANSACTION_onewayFoo),
+                /* flags= */ intThat(OnewayMatcher(true)),
+                /* exception= */ eq(myException),
+            )
+        // No unexpected calls
+        verify(mInterfaceImpl).onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    @Test
+    fun testOnewayMethod_ifThrowsRemoteException_HandlerCalled() {
+        val myException = RemoteException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).onewayFoo(anyInt())
+
+        dispatchActionCall("onewayFoo")
+
+        verify(mInterfaceImpl, timeout(TIMEOUT_DURATION_MS))
+            .onUnhandledExceptionVisible(
+                /* transactionCode = */ eq(TRANSACTION_onewayFoo),
+                /* flags= */ intThat(OnewayMatcher(true)),
+                /* exception= */ eq(myException),
+            )
+        // No unexpected calls
+        verify(mInterfaceImpl).onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    // All exceptions are uncaught for oneway
+    @Test
+    fun testOnewayMethod_ifThrowsSecurityException_HandlerCalled() {
+        val myException = SecurityException("Test exception")
+        doThrow(myException).`when`(mInterfaceImpl).onewayFoo(anyInt())
+
+        dispatchActionCall("onewayFoo")
+
+        verify(mInterfaceImpl, timeout(TIMEOUT_DURATION_MS))
+            .onUnhandledExceptionVisible(
+                /* transactionCode = */ eq(TRANSACTION_onewayFoo),
+                /* flags= */ intThat(OnewayMatcher(true)),
+                /* exception= */ eq(myException),
+            )
+        // No unexpected calls
+        verify(mInterfaceImpl).onUnhandledExceptionVisible(anyInt(), anyInt(), any())
+    }
+
+    private fun dispatchActionCall(methodName: String) =
+        Intent(ACTION_CALL_METHOD).apply {
+            putExtras(
+                Bundle().apply {
+                    putBinder(EXTRA_BINDER, mInterfaceImpl as IBinder)
+                    putString(EXTRA_METHOD_NAME, methodName)
+                }
+            )
+            setClassName(PACKAGE_NAME, CallMethodsReceiver::class.java.getName())
+        }.let { mContext.sendBroadcast(it) }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 786f1e8..ba6f62c 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -38,7 +38,6 @@
 import android.graphics.RectF;
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseArray;
-import android.view.SurfaceControl.Transaction;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
@@ -79,7 +78,6 @@
     private SurfaceControl mNavLeash;
     private InsetsState mInsetsState;
 
-    @Mock Transaction mMockTransaction;
     @Mock InsetsController mMockController;
     @Mock WindowInsetsAnimationControlListener mMockListener;
 
@@ -98,16 +96,14 @@
         mInsetsState.getOrCreateSource(ID_NAVIGATION_BAR, navigationBars())
                 .setFrame(new Rect(400, 0, 500, 500));
         InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ID_STATUS_BAR,
-                WindowInsets.Type.statusBars(), mInsetsState,
-                () -> mMockTransaction, mMockController);
+                WindowInsets.Type.statusBars(), mInsetsState, mMockController);
         topConsumer.setControl(
                 new InsetsSourceControl(ID_STATUS_BAR, WindowInsets.Type.statusBars(),
                         mStatusLeash, true, new Point(0, 0), Insets.of(0, 100, 0, 0)),
                 new int[1], new int[1]);
 
         InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ID_NAVIGATION_BAR,
-                WindowInsets.Type.navigationBars(), mInsetsState,
-                () -> mMockTransaction, mMockController);
+                WindowInsets.Type.navigationBars(), mInsetsState, mMockController);
         navConsumer.setControl(
                 new InsetsSourceControl(ID_NAVIGATION_BAR, WindowInsets.Type.navigationBars(),
                         mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)),
@@ -131,8 +127,9 @@
 
         mController = new InsetsAnimationControlImpl(controls,
                 new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
-                mMockController, spec /* insetsAnimationSpecCreator */, 0 /* animationType */,
-                0 /* layoutInsetsDuringAnimation */, null /* translator */, null /* statsToken */);
+                mMockController, mMockController, spec /* insetsAnimationSpecCreator */,
+                0 /* animationType */, 0 /* layoutInsetsDuringAnimation */, null /* translator */,
+                null /* statsToken */);
         mController.setReadyDispatched(true);
     }
 
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index bec8b1f..4516e9c 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -63,7 +63,6 @@
 import android.graphics.Rect;
 import android.os.CancellationSignal;
 import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
 import android.view.WindowManager.BadTokenException;
@@ -138,8 +137,7 @@
             mTestHost = spy(new TestHost(mViewRoot));
             mController = new InsetsController(mTestHost, (controller, id, type) -> {
                 if (!Flags.refactorInsetsController() && type == ime()) {
-                    return new InsetsSourceConsumer(id, type, controller.getState(),
-                            Transaction::new, controller) {
+                    return new InsetsSourceConsumer(id, type, controller.getState(), controller) {
 
                         private boolean mImeRequestedShow;
 
@@ -155,8 +153,7 @@
                         }
                     };
                 } else {
-                    return new InsetsSourceConsumer(id, type, controller.getState(),
-                            Transaction::new, controller);
+                    return new InsetsSourceConsumer(id, type, controller.getState(), controller);
                 }
             }, mTestHandler);
             final Rect rect = new Rect(5, 5, 5, 5);
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 655cb45..d6d45e8 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -28,10 +28,7 @@
 import static junit.framework.TestCase.assertTrue;
 
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.app.Instrumentation;
 import android.content.Context;
@@ -39,7 +36,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager.BadTokenException;
 import android.view.WindowManager.LayoutParams;
 import android.view.inputmethod.ImeTracker;
@@ -51,7 +47,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
@@ -75,9 +70,9 @@
 
     private SurfaceSession mSession = new SurfaceSession();
     private SurfaceControl mLeash;
-    @Mock Transaction mMockTransaction;
     private InsetsSource mSpyInsetsSource;
     private boolean mRemoveSurfaceCalled = false;
+    private boolean mSurfaceParamsApplied = false;
     private InsetsController mController;
     private InsetsState mState;
     private ViewRootImpl mViewRoot;
@@ -102,9 +97,14 @@
             mSpyInsetsSource = Mockito.spy(new InsetsSource(ID_STATUS_BAR, statusBars()));
             mState.addSource(mSpyInsetsSource);
 
-            mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot));
-            mConsumer = new InsetsSourceConsumer(ID_STATUS_BAR, statusBars(), mState,
-                    () -> mMockTransaction, mController) {
+            mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot)) {
+                @Override
+                public void applySurfaceParams(
+                        final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+                    mSurfaceParamsApplied = true;
+                }
+            };
+            mConsumer = new InsetsSourceConsumer(ID_STATUS_BAR, statusBars(), mState, mController) {
                 @Override
                 public void removeSurface() {
                     super.removeSurface();
@@ -148,8 +148,7 @@
         InsetsState state = new InsetsState();
         InsetsController controller = new InsetsController(new ViewRootInsetsControllerHost(
                 mViewRoot));
-        InsetsSourceConsumer consumer = new InsetsSourceConsumer(
-                ID_IME, ime(), state, null, controller);
+        InsetsSourceConsumer consumer = new InsetsSourceConsumer(ID_IME, ime(), state, controller);
 
         InsetsSource source = new InsetsSource(ID_IME, ime());
         source.setFrame(0, 1, 2, 3);
@@ -182,9 +181,9 @@
     public void testRestore() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mConsumer.setControl(null, new int[1], new int[1]);
-            reset(mMockTransaction);
+            mSurfaceParamsApplied = false;
             mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
-            verifyZeroInteractions(mMockTransaction);
+            assertFalse(mSurfaceParamsApplied);
             int[] hideTypes = new int[1];
             mConsumer.setControl(
                     new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
@@ -200,8 +199,9 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
             mConsumer.setControl(null, new int[1], new int[1]);
-            reset(mMockTransaction);
-            verifyZeroInteractions(mMockTransaction);
+            mLeash = new SurfaceControl.Builder(mSession)
+                    .setName("testSurface")
+                    .build();
             mRemoveSurfaceCalled = false;
             int[] hideTypes = new int[1];
             mConsumer.setControl(
@@ -221,8 +221,7 @@
             ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot);
             InsetsController insetsController = new InsetsController(host, (ic, id, type) -> {
                 if (type == ime()) {
-                    return new InsetsSourceConsumer(ID_IME, ime(), state,
-                            () -> mMockTransaction, ic) {
+                    return new InsetsSourceConsumer(ID_IME, ime(), state, ic) {
                         @Override
                         public int requestShow(boolean fromController,
                                 ImeTracker.Token statsToken) {
@@ -230,14 +229,14 @@
                         }
                     };
                 }
-                return new InsetsSourceConsumer(id, type, ic.getState(), Transaction::new, ic);
+                return new InsetsSourceConsumer(id, type, ic.getState(), ic);
             }, host.getHandler());
             InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ID_IME, ime());
 
             // Initial IME insets source control with its leash.
             imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
                     false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
-            reset(mMockTransaction);
+            mSurfaceParamsApplied = false;
 
             // Verify when the app requests controlling show IME animation, the IME leash
             // visibility won't be updated when the consumer received the same leash in setControl.
@@ -246,7 +245,7 @@
             assertEquals(ANIMATION_TYPE_USER, insetsController.getAnimationType(ime()));
             imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
                     true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
-            verify(mMockTransaction, never()).show(mLeash);
+            assertFalse(mSurfaceParamsApplied);
         });
     }
 }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index e240a08..6327211 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -71,7 +71,6 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
 import android.os.SystemProperties;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -1599,7 +1598,6 @@
                 nativeCreateASurfaceControlFromSurface(mViewRootImpl.mSurface));
     }
 
-    @EnableFlags(Flags.FLAG_INSETS_CONTROL_SEQ)
     @Test
     public void testHandleInsetsControlChanged() {
         mView = new View(sContext);
diff --git a/core/tests/coretests/src/android/view/WindowLayoutTests.java b/core/tests/coretests/src/android/view/WindowLayoutTests.java
index 5cac98d..d4693e6 100644
--- a/core/tests/coretests/src/android/view/WindowLayoutTests.java
+++ b/core/tests/coretests/src/android/view/WindowLayoutTests.java
@@ -413,4 +413,19 @@
         assertInsetByTopBottom(0, 0, mFrames.parentFrame);
         assertInsetByTopBottom(0, 0, mFrames.frame);
     }
+
+    @Test
+    public void windowBoundsOutsideDisplayCutoutSafe() {
+        addDisplayCutout();
+        mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+        mWindowBounds.set(0, -1000, DISPLAY_WIDTH, 0);
+        computeFrames();
+
+        assertRect(WATERFALL_INSETS.left, 0, DISPLAY_WIDTH - WATERFALL_INSETS.right, 0,
+                mFrames.displayFrame);
+        assertRect(WATERFALL_INSETS.left, 0, DISPLAY_WIDTH - WATERFALL_INSETS.right, 0,
+                mFrames.parentFrame);
+        assertRect(WATERFALL_INSETS.left, 0, DISPLAY_WIDTH - WATERFALL_INSETS.right, 0,
+                mFrames.frame);
+    }
 }
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 46dfcb5..25f458e 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -103,15 +103,7 @@
 
     private int mCallbackInfoCalls = 0;
 
-    private final BackMotionEvent mBackEvent = new BackMotionEvent(
-            /* touchX = */ 0,
-            /* touchY = */ 0,
-            /* progress = */ 0,
-            /* velocityX = */ 0,
-            /* velocityY = */ 0,
-            /* triggerBack = */ false,
-            /* swipeEdge = */ BackEvent.EDGE_LEFT,
-            /* departingAnimationTarget = */ null);
+    private final BackMotionEvent mBackEvent = backMotionEventFrom(/* progress */ 0f);
     private final MotionEvent mMotionEvent =
             MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 100, 100, 0);
 
@@ -558,6 +550,7 @@
         OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
 
         callbackInfo.getCallback().onBackStarted(mBackEvent);
+        callbackInfo.getCallback().onBackProgressed(backMotionEventFrom(/* progress */ 0.5f));
         waitForIdle();
         assertTrue(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
 
@@ -575,6 +568,18 @@
         assertFalse(mDispatcher.mProgressAnimator.isBackAnimationInProgress());
     }
 
+    private BackMotionEvent backMotionEventFrom(float progress) {
+        return new BackMotionEvent(
+                /* touchX = */ 0,
+                /* touchY = */ 0,
+                /* progress = */ progress,
+                /* velocityX = */ 0,
+                /* velocityY = */ 0,
+                /* triggerBack = */ false,
+                /* swipeEdge = */ BackEvent.EDGE_LEFT,
+                /* departingAnimationTarget = */ null);
+    }
+
     private void verifyImeCallackRegistrations() throws RemoteException {
         // verify default callback is replaced with ImeBackAnimationController
         mDispatcher.registerOnBackInvokedCallbackUnchecked(mDefaultImeCallback, PRIORITY_DEFAULT);
diff --git a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java b/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
index dd40695..a311d0d 100644
--- a/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
+++ b/core/tests/coretests/src/android/window/flags/DesktopModeFlagsTest.java
@@ -17,9 +17,13 @@
 package android.window.flags;
 
 import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE;
+import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF;
+import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_ON;
+import static android.window.flags.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET;
+import static android.window.flags.DesktopModeFlags.ToggleOverride.fromSetting;
 
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
-import static com.android.window.flags.Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS;
 import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -72,267 +76,287 @@
     @Test
     @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
+    public void isTrue_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_OFF_SETTING);
         // In absence of dev options, follow flag
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
     }
 
 
     @Test
     @DisableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
+    public void isTrue_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_ON_SETTING);
 
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_overrideUnset_featureFlagOn_returnsTrue() {
+    public void isTrue_overrideUnset_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For overridableFlag, for unset overrides, follow flag
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_overrideUnset_featureFlagOff_returnsFalse() {
+    public void isTrue_overrideUnset_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For overridableFlag, for unset overrides, follow flag
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_noOverride_featureFlagOn_returnsTrue() {
+    public void isTrue_noOverride_featureFlagOn_returnsTrue() {
         setOverride(null);
 
         // For overridableFlag, in absence of overrides, follow flag
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_noOverride_featureFlagOff_returnsFalse() {
+    public void isTrue_noOverride_featureFlagOff_returnsFalse() {
         setOverride(null);
 
         // For overridableFlag, in absence of overrides, follow flag
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_unrecognizableOverride_featureFlagOn_returnsTrue() {
+    public void isTrue_unrecognizableOverride_featureFlagOn_returnsTrue() {
         setOverride(-2);
 
         // For overridableFlag, for unrecognized overrides, follow flag
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_unrecognizableOverride_featureFlagOff_returnsFalse() {
+    public void isTrue_unrecognizableOverride_featureFlagOff_returnsFalse() {
         setOverride(-2);
 
         // For overridableFlag, for unrecognizable overrides, follow flag
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_overrideOff_featureFlagOn_returnsFalse() {
+    public void isTrue_overrideOff_featureFlagOn_returnsFalse() {
         setOverride(OVERRIDE_OFF_SETTING);
 
         // For overridableFlag, follow override if they exist
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_overrideOn_featureFlagOff_returnsTrue() {
+    public void isTrue_overrideOn_featureFlagOff_returnsTrue() {
         setOverride(OVERRIDE_ON_SETTING);
 
         // For overridableFlag, follow override if they exist
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    public void isEnabled_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {
+    public void isTrue_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {
         setOverride(OVERRIDE_OFF_SETTING);
 
         // For overridableFlag, follow override if they exist
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
 
         setOverride(OVERRIDE_ON_SETTING);
 
         // Keep overrides constant through the process
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isFalse();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {
+    public void isTrue_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {
         setOverride(OVERRIDE_ON_SETTING);
 
         // For overridableFlag, follow override if they exist
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
 
         setOverride(OVERRIDE_OFF_SETTING);
 
         // Keep overrides constant through the process
-        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isEnabled()).isTrue();
+        assertThat(ENABLE_DESKTOP_WINDOWING_MODE.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS})
-    public void isEnabled_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS})
+    public void isTrue_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-    public void isEnabled_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
+    public void isTrue_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_UNSET_SETTING);
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
-    public void isEnabled_dwFlagOn_overrideOn_featureFlagOn_returnsTrue() {
+    public void isTrue_dwFlagOn_overrideOn_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_ON_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-    public void isEnabled_dwFlagOn_overrideOn_featureFlagOff_returnsFalse() {
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
+    public void isTrue_dwFlagOn_overrideOn_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_ON_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
-    public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsTrue() {
+    public void isTrue_dwFlagOn_overrideOff_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_OFF_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
-    @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
-    public void isEnabled_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() {
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS)
+    public void isTrue_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_OFF_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() {
+    public void isTrue_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags({
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
-    public void isEnabled_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() {
+    public void isTrue_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_UNSET_SETTING);
 
         // For unset overrides, follow flag
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() {
+    public void isTrue_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_ON_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags({
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
-    public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnFalse() {
+    public void isTrue_dwFlagOff_overrideOn_featureFlagOff_returnFalse() {
         setOverride(OVERRIDE_ON_SETTING);
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
     }
 
     @Test
     @EnableFlags({
             FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-    public void isEnabled_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() {
+    public void isTrue_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_OFF_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isTrue();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isTrue();
     }
 
     @Test
     @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
     @DisableFlags({
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-            FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
+            FLAG_ENABLE_DESKTOP_WINDOWING_TRANSITIONS
     })
-    public void isEnabled_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() {
+    public void isTrue_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() {
         setOverride(OVERRIDE_OFF_SETTING);
 
         // When toggle override matches its default state (dw flag), don't override flags
-        assertThat(DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()).isFalse();
+        assertThat(DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TRANSITIONS.isTrue()).isFalse();
+    }
+
+    @Test
+    public void fromSetting_validInt_returnsToggleOverride() {
+        assertThat(fromSetting(0, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_OFF);
+        assertThat(fromSetting(1, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_ON);
+        assertThat(fromSetting(-1, OVERRIDE_ON)).isEqualTo(OVERRIDE_UNSET);
+    }
+
+    @Test
+    public void fromSetting_invalidInt_returnsFallback() {
+        assertThat(fromSetting(2, OVERRIDE_ON)).isEqualTo(OVERRIDE_ON);
+        assertThat(fromSetting(-2, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_UNSET);
+    }
+
+    @Test
+    public void getSetting_returnsToggleOverrideInteger() {
+        assertThat(OVERRIDE_OFF.getSetting()).isEqualTo(0);
+        assertThat(OVERRIDE_ON.getSetting()).isEqualTo(1);
+        assertThat(OVERRIDE_UNSET.getSetting()).isEqualTo(-1);
     }
 
     private void setOverride(Integer setting) {
diff --git a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
index 005538a..d9e90fa 100644
--- a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
+++ b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
@@ -29,6 +29,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -78,6 +79,16 @@
     }
 
     @Test
+    public void testNegatedDisabledFlag() {
+        assertThat(mResources.getBoolean(R.bool.bool5)).isTrue();
+    }
+
+    @Test
+    public void testNegatedEnabledFlag() {
+        assertThat(mResources.getBoolean(R.bool.bool6)).isTrue();
+    }
+
+    @Test
     public void testFlagEnabledDifferentCompilationUnit() {
         assertThat(mResources.getBoolean(R.bool.bool3)).isTrue();
     }
@@ -94,6 +105,26 @@
     }
 
     @Test
+    public void testDirectoryEnabledFlag() {
+        assertThat(mResources.getBoolean(R.bool.bool8)).isTrue();
+    }
+
+    @Test
+    public void testDirectoryDisabledFlag() {
+        assertThat(mResources.getBoolean(R.bool.bool7)).isTrue();
+    }
+
+    @Test
+    public void testDirectoryNegatedEnabledFlag() {
+        assertThat(mResources.getBoolean(R.bool.bool9)).isTrue();
+    }
+
+    @Test
+    public void testDirectoryNegatedDisabledFlag() {
+        assertThat(mResources.getBoolean(R.bool.bool10)).isTrue();
+    }
+
+    @Test
     public void testLayoutWithDisabledElements() {
         LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.layout1, null);
         assertThat(ll).isNotNull();
@@ -102,6 +133,24 @@
         assertThat((View) ll.findViewById(R.id.text2)).isNotNull();
     }
 
+    @Test
+    public void testEnabledFlagLayoutOverrides() {
+        LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.layout3, null);
+        assertThat(ll).isNotNull();
+        assertThat((View) ll.findViewById(R.id.text1)).isNotNull();
+        assertThat(((TextView) ll.findViewById(R.id.text1)).getText()).isEqualTo("foobar");
+    }
+
+    @Test(expected = Resources.NotFoundException.class)
+    public void testDisabledLayout() {
+        getLayoutInflater().inflate(R.layout.layout2, null);
+    }
+
+    @Test(expected = Resources.NotFoundException.class)
+    public void testDisabledDrawable() {
+        mResources.getDrawable(R.drawable.removedpng);
+    }
+
     private LayoutInflater getLayoutInflater() {
         ContextWrapper c = new ContextWrapper(mContext) {
             private LayoutInflater mInflater;
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
index 89acbc7..0ce403e 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java
@@ -35,6 +35,7 @@
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.os.Parcel;
+import android.view.Display;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -64,10 +65,12 @@
         assertNull("Bitmap was expected to be null", out.getBitmap());
         assertNull("Bounds were expected to be null", out.getBoundsInScreen());
         assertEquals(Insets.NONE, out.getInsets());
+        assertEquals(Display.INVALID_DISPLAY, out.getDisplayId());
     }
 
     @Test
     public void testProvidedScreenshot() {
+        int displayId = 5;
         Bitmap bitmap = makeHardwareBitmap(50, 50);
         ScreenshotRequest in =
                 new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
@@ -77,6 +80,7 @@
                         .setBitmap(bitmap)
                         .setBoundsOnScreen(new Rect(10, 10, 60, 60))
                         .setInsets(Insets.of(2, 3, 4, 5))
+                        .setDisplayId(displayId)
                         .build();
 
         Parcel parcel = Parcel.obtain();
@@ -92,6 +96,7 @@
         assertTrue("Bitmaps should be equal", out.getBitmap().sameAs(bitmap));
         assertEquals(new Rect(10, 10, 60, 60), out.getBoundsInScreen());
         assertEquals(Insets.of(2, 3, 4, 5), out.getInsets());
+        assertEquals(displayId, out.getDisplayId());
     }
 
     @Test
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index d98120f..75aca1b 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -23,11 +23,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.platform.test.ravenwood.RavenwoodRule;
+import android.platform.test.ravenwood.RavenwoodConfig;
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Objects;
@@ -35,16 +34,18 @@
 import java.util.concurrent.TimeUnit;
 
 public class SystemPropertiesTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setSystemPropertyMutable(KEY, null)
-            .setSystemPropertyMutable(UNSET_KEY, null)
-            .setSystemPropertyMutable(PERSIST_KEY, null)
-            .build();
-
     private static final String KEY = "sys.testkey";
     private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
     private static final String PERSIST_KEY = "persist.sys.testkey";
+    private static final String NONEXIST_KEY = "doesnotexist_2341431";
+
+    @RavenwoodConfig.Config
+    public static final RavenwoodConfig mRavenwood = new RavenwoodConfig.Builder()
+            .setSystemPropertyMutable(KEY, null)
+            .setSystemPropertyMutable(UNSET_KEY, null)
+            .setSystemPropertyMutable(PERSIST_KEY, null)
+            .setSystemPropertyImmutable(NONEXIST_KEY, null)
+            .build();
 
     @Test
     @SmallTest
@@ -117,7 +118,7 @@
     @SmallTest
     public void testHandle() throws Exception {
         String value;
-        SystemProperties.Handle handle = SystemProperties.find("doesnotexist_2341431");
+        SystemProperties.Handle handle = SystemProperties.find(NONEXIST_KEY);
         assertNull(handle);
         SystemProperties.set(KEY, "abc");
         handle = SystemProperties.find(KEY);
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index e07471c..6d31578 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -128,22 +128,6 @@
     private static final WeakHashMap<Bitmap, Void> sAllBitmaps = new WeakHashMap<>();
 
     /**
-     * @hide
-     */
-    private static NativeAllocationRegistry getRegistry(boolean malloc, long size) {
-        final long free = nativeGetNativeFinalizer();
-        if (com.android.libcore.Flags.nativeMetrics()) {
-            Class cls = Bitmap.class;
-            return malloc ? NativeAllocationRegistry.createMalloced(cls, free, size)
-                          : NativeAllocationRegistry.createNonmalloced(cls, free, size);
-        } else {
-            ClassLoader loader = Bitmap.class.getClassLoader();
-            return malloc ? NativeAllocationRegistry.createMalloced(loader, free, size)
-                          : NativeAllocationRegistry.createNonmalloced(loader, free, size);
-        }
-    }
-
-    /**
      * Private constructor that must receive an already allocated native bitmap
      * int (pointer).
      */
@@ -167,6 +151,7 @@
         mWidth = width;
         mHeight = height;
         mRequestPremultiplied = requestPremultiplied;
+
         mNinePatchChunk = ninePatchChunk;
         mNinePatchInsets = ninePatchInsets;
         if (density >= 0) {
@@ -174,9 +159,17 @@
         }
 
         mNativePtr = nativeBitmap;
-        final int allocationByteCount = getAllocationByteCount();
-        getRegistry(fromMalloc, allocationByteCount).registerNativeAllocation(this, mNativePtr);
 
+        final int allocationByteCount = getAllocationByteCount();
+        NativeAllocationRegistry registry;
+        if (fromMalloc) {
+            registry = NativeAllocationRegistry.createMalloced(
+                    Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount);
+        } else {
+            registry = NativeAllocationRegistry.createNonmalloced(
+                    Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount);
+        }
+        registry.registerNativeAllocation(this, nativeBitmap);
         synchronized (Bitmap.class) {
           sAllBitmaps.put(this, null);
         }
diff --git a/graphics/java/android/graphics/GraphicsStatsService.java b/graphics/java/android/graphics/GraphicsStatsService.java
index dc785c5..7a012bc 100644
--- a/graphics/java/android/graphics/GraphicsStatsService.java
+++ b/graphics/java/android/graphics/GraphicsStatsService.java
@@ -311,7 +311,7 @@
                 Log.w(TAG, "Unable to create path: '" + parent.getAbsolutePath() + "'");
                 return;
             }
-            nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.mPackageName,
+            nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.mUid, buffer.mInfo.mPackageName,
                     buffer.mInfo.mVersionCode, buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
                     buffer.mData);
         }
@@ -405,7 +405,7 @@
             HistoricalBuffer buffer = buffers.get(i);
             File path = pathForApp(buffer.mInfo);
             skipFiles.add(path);
-            nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.mPackageName,
+            nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.mUid, buffer.mInfo.mPackageName,
                     buffer.mInfo.mVersionCode,  buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
                     buffer.mData);
         }
@@ -469,21 +469,23 @@
 
     private static native int nGetAshmemSize();
     private static native long nCreateDump(int outFd, boolean isProto);
-    private static native void nAddToDump(long dump, String path, String packageName,
+    private static native void nAddToDump(long dump, String path, int uid, String packageName,
             long versionCode, long startTime, long endTime, byte[] data);
     private static native void nAddToDump(long dump, String path);
     private static native void nFinishDump(long dump);
     private static native void nFinishDumpInMemory(long dump, long pulledData, boolean lastFullDay);
-    private static native void nSaveBuffer(String path, String packageName, long versionCode,
-            long startTime, long endTime, byte[] data);
+    private static native void nSaveBuffer(String path, int uid, String packageName,
+            long versionCode, long startTime, long endTime, byte[] data);
 
     private final class BufferInfo {
+        final int mUid;
         final String mPackageName;
         final long mVersionCode;
         long mStartTime;
         long mEndTime;
 
-        BufferInfo(String packageName, long versionCode, long startTime) {
+        BufferInfo(int uid, String packageName, long versionCode, long startTime) {
+            this.mUid = uid;
             this.mPackageName = packageName;
             this.mVersionCode = versionCode;
             this.mStartTime = startTime;
@@ -502,7 +504,7 @@
         ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName,
                 long versionCode)
                 throws RemoteException, IOException {
-            mInfo = new BufferInfo(packageName, versionCode, System.currentTimeMillis());
+            mInfo = new BufferInfo(uid, packageName, versionCode, System.currentTimeMillis());
             mUid = uid;
             mPid = pid;
             mCallback = token;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 37f0067..0896138 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -16,12 +16,11 @@
 
 package androidx.window.common;
 
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
 import static androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
 import static androidx.window.common.layout.CommonFoldingFeature.parseListFromString;
 
-import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
@@ -31,16 +30,23 @@
 import android.util.Log;
 import android.util.SparseIntArray;
 
+import androidx.annotation.BinderThread;
+import androidx.annotation.GuardedBy;
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.window.common.layout.CommonFoldingFeature;
 import androidx.window.common.layout.DisplayFoldFeatureCommon;
 
 import com.android.internal.R;
+import com.android.window.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -55,13 +61,6 @@
     private static final boolean DEBUG = false;
 
     /**
-     * Emulated device state
-     * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} to
-     * {@link CommonFoldingFeature.State} map.
-     */
-    private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
-
-    /**
      * Device state received via
      * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)}.
      * The identifier returned through {@link DeviceState#getIdentifier()} may not correspond 1:1
@@ -71,23 +70,40 @@
      * "rear display". Concurrent mode for example is activated via public API and can be active in
      * both the "open" and "half folded" device states.
      */
-    private DeviceState mCurrentDeviceState = new DeviceState(
-            new DeviceState.Configuration.Builder(INVALID_DEVICE_STATE_IDENTIFIER,
-                    "INVALID").build());
+    // TODO: b/337820752 - Add @GuardedBy("mCurrentDeviceStateLock") after flag cleanup.
+    private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE;
 
-    private List<DeviceState> mSupportedStates;
+    /**
+     * Lock to synchronize access to {@link #mCurrentDeviceState}.
+     *
+     * <p>This lock is used to ensure thread-safety when accessing and modifying the
+     * {@link #mCurrentDeviceState} field. It is acquired by both the binder thread (if
+     * {@link Flags#wlinfoOncreate()} is enabled) and the main thread (if
+     * {@link Flags#wlinfoOncreate()} is disabled) to prevent race conditions and
+     * ensure data consistency.
+     */
+    private final Object mCurrentDeviceStateLock = new Object();
 
     @NonNull
     private final RawFoldingFeatureProducer mRawFoldSupplier;
 
-    private final boolean mIsHalfOpenedSupported;
+    @NonNull
+    private final DeviceStateMapper mDeviceStateMapper;
 
-    private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
+    @VisibleForTesting
+    final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
+        // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the getData()
+        // implementation. See https://errorprone.info/bugpattern/GuardedBy for limitations.
+        @SuppressWarnings("GuardedBy")
+        @BinderThread // When Flags.wlinfoOncreate() is enabled.
+        @MainThread // When Flags.wlinfoOncreate() is disabled.
         @Override
         public void onDeviceStateChanged(@NonNull DeviceState state) {
-            mCurrentDeviceState = state;
-            mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
-                    .this::notifyFoldingFeatureChange);
+            synchronized (mCurrentDeviceStateLock) {
+                mCurrentDeviceState = state;
+                mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this
+                        ::notifyFoldingFeatureChangeLocked);
+            }
         }
     };
 
@@ -95,41 +111,14 @@
             @NonNull RawFoldingFeatureProducer rawFoldSupplier,
             @NonNull DeviceStateManager deviceStateManager) {
         mRawFoldSupplier = rawFoldSupplier;
-        String[] deviceStatePosturePairs = context.getResources()
-                .getStringArray(R.array.config_device_state_postures);
-        mSupportedStates = deviceStateManager.getSupportedDeviceStates();
-        boolean isHalfOpenedSupported = false;
-        for (String deviceStatePosturePair : deviceStatePosturePairs) {
-            String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
-            if (deviceStatePostureMapping.length != 2) {
-                if (DEBUG) {
-                    Log.e(TAG, "Malformed device state posture pair: "
-                            + deviceStatePosturePair);
-                }
-                continue;
-            }
+        mDeviceStateMapper =
+                new DeviceStateMapper(context, deviceStateManager.getSupportedDeviceStates());
 
-            int deviceState;
-            int posture;
-            try {
-                deviceState = Integer.parseInt(deviceStatePostureMapping[0]);
-                posture = Integer.parseInt(deviceStatePostureMapping[1]);
-            } catch (NumberFormatException e) {
-                if (DEBUG) {
-                    Log.e(TAG, "Failed to parse device state or posture: "
-                                    + deviceStatePosturePair,
-                            e);
-                }
-                continue;
-            }
-            isHalfOpenedSupported = isHalfOpenedSupported
-                    || posture == CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
-            mDeviceStateToPostureMap.put(deviceState, posture);
-        }
-        mIsHalfOpenedSupported = isHalfOpenedSupported;
-        if (mDeviceStateToPostureMap.size() > 0) {
+        if (!mDeviceStateMapper.isDeviceStateToPostureMapEmpty()) {
+            final Executor executor =
+                    Flags.wlinfoOncreate() ? Runnable::run : context.getMainExecutor();
             Objects.requireNonNull(deviceStateManager)
-                    .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
+                    .registerCallback(executor, mDeviceStateCallback);
         }
     }
 
@@ -137,50 +126,51 @@
      * Add a callback to mCallbacks if there is no device state. This callback will be run
      * once a device state is set. Otherwise,run the callback immediately.
      */
-    private void runCallbackWhenValidState(@NonNull Consumer<List<CommonFoldingFeature>> callback,
-            String displayFeaturesString) {
-        if (isCurrentStateValid()) {
-            callback.accept(calculateFoldingFeature(displayFeaturesString));
+    private void runCallbackWhenValidState(@NonNull DeviceState state,
+            @NonNull Consumer<List<CommonFoldingFeature>> callback,
+            @NonNull String displayFeaturesString) {
+        if (mDeviceStateMapper.isDeviceStateValid(state)) {
+            callback.accept(calculateFoldingFeature(state, displayFeaturesString));
         } else {
             // This callback will be added to mCallbacks and removed once it runs once.
-            AcceptOnceConsumer<List<CommonFoldingFeature>> singleRunCallback =
+            final AcceptOnceConsumer<List<CommonFoldingFeature>> singleRunCallback =
                     new AcceptOnceConsumer<>(this, callback);
             addDataChangedCallback(singleRunCallback);
         }
     }
 
-    /**
-     * Checks to find {@link DeviceStateManagerFoldingFeatureProducer#mCurrentDeviceState} in the
-     * {@link DeviceStateManagerFoldingFeatureProducer#mDeviceStateToPostureMap} which was
-     * initialized in the constructor of {@link DeviceStateManagerFoldingFeatureProducer}.
-     * Returns a boolean value of whether the device state is valid.
-     */
-    private boolean isCurrentStateValid() {
-        // If the device state is not found in the map, indexOfKey returns a negative number.
-        return mDeviceStateToPostureMap.indexOfKey(mCurrentDeviceState.getIdentifier()) >= 0;
-    }
-
+    // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the implementation of
+    // addDataChangedCallback(). See https://errorprone.info/bugpattern/GuardedBy for limitations.
+    @SuppressWarnings("GuardedBy")
     @Override
     protected void onListenersChanged() {
         super.onListenersChanged();
-        if (hasListeners()) {
-            mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChange);
-        } else {
-            mCurrentDeviceState = new DeviceState(
-                    new DeviceState.Configuration.Builder(INVALID_DEVICE_STATE_IDENTIFIER,
-                            "INVALID").build());
-            mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChange);
+        synchronized (mCurrentDeviceStateLock) {
+            if (hasListeners()) {
+                mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChangeLocked);
+            } else {
+                mCurrentDeviceState = INVALID_DEVICE_STATE;
+                mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChangeLocked);
+            }
+        }
+    }
+
+    @NonNull
+    private DeviceState getCurrentDeviceState() {
+        synchronized (mCurrentDeviceStateLock) {
+            return mCurrentDeviceState;
         }
     }
 
     @NonNull
     @Override
     public Optional<List<CommonFoldingFeature>> getCurrentData() {
-        Optional<String> displayFeaturesString = mRawFoldSupplier.getCurrentData();
-        if (!isCurrentStateValid()) {
+        final Optional<String> displayFeaturesString = mRawFoldSupplier.getCurrentData();
+        final DeviceState state = getCurrentDeviceState();
+        if (!mDeviceStateMapper.isDeviceStateValid(state) || displayFeaturesString.isEmpty()) {
             return Optional.empty();
         } else {
-            return displayFeaturesString.map(this::calculateFoldingFeature);
+            return Optional.of(calculateFoldingFeature(state, displayFeaturesString.get()));
         }
     }
 
@@ -191,7 +181,7 @@
      */
     @NonNull
     public List<CommonFoldingFeature> getFoldsWithUnknownState() {
-        Optional<String> optionalFoldingFeatureString = mRawFoldSupplier.getCurrentData();
+        final Optional<String> optionalFoldingFeatureString = mRawFoldSupplier.getCurrentData();
 
         if (optionalFoldingFeatureString.isPresent()) {
             return CommonFoldingFeature.parseListFromString(
@@ -201,7 +191,6 @@
         return Collections.emptyList();
     }
 
-
     /**
      * Returns the list of supported {@link DisplayFoldFeatureCommon} calculated from the
      * {@link DeviceStateManagerFoldingFeatureProducer}.
@@ -218,16 +207,16 @@
         return foldFeatures;
     }
 
-
     /**
      * Returns {@code true} if the device supports half-opened mode, {@code false} otherwise.
      */
     public boolean isHalfOpenedSupported() {
-        return mIsHalfOpenedSupported;
+        return mDeviceStateMapper.mIsHalfOpenedSupported;
     }
 
     /**
      * Adds the data to the storeFeaturesConsumer when the data is ready.
+     *
      * @param storeFeaturesConsumer a consumer to collect the data when it is first available.
      */
     @Override
@@ -236,38 +225,123 @@
             if (TextUtils.isEmpty(displayFeaturesString)) {
                 storeFeaturesConsumer.accept(new ArrayList<>());
             } else {
-                runCallbackWhenValidState(storeFeaturesConsumer, displayFeaturesString);
+                final DeviceState state = getCurrentDeviceState();
+                runCallbackWhenValidState(state, storeFeaturesConsumer, displayFeaturesString);
             }
         });
     }
 
-    private void notifyFoldingFeatureChange(String displayFeaturesString) {
-        if (!isCurrentStateValid()) {
+    @GuardedBy("mCurrentDeviceStateLock")
+    private void notifyFoldingFeatureChangeLocked(String displayFeaturesString) {
+        final DeviceState state = mCurrentDeviceState;
+        if (!mDeviceStateMapper.isDeviceStateValid(state)) {
             return;
         }
         if (TextUtils.isEmpty(displayFeaturesString)) {
             notifyDataChanged(new ArrayList<>());
         } else {
-            notifyDataChanged(calculateFoldingFeature(displayFeaturesString));
+            notifyDataChanged(calculateFoldingFeature(state, displayFeaturesString));
         }
     }
 
-    private List<CommonFoldingFeature> calculateFoldingFeature(String displayFeaturesString) {
-        return parseListFromString(displayFeaturesString, currentHingeState());
-    }
-
-    @CommonFoldingFeature.State
-    private int currentHingeState() {
+    @NonNull
+    private List<CommonFoldingFeature> calculateFoldingFeature(@NonNull DeviceState deviceState,
+            @NonNull String displayFeaturesString) {
         @CommonFoldingFeature.State
-        int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState.getIdentifier(),
-                COMMON_STATE_UNKNOWN);
+        final int hingeState = mDeviceStateMapper.getHingeState(deviceState);
+        return parseListFromString(displayFeaturesString, hingeState);
+    }
 
-        if (posture == CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
-            posture = mDeviceStateToPostureMap.get(
-                    DeviceStateUtil.calculateBaseStateIdentifier(mCurrentDeviceState,
-                            mSupportedStates), COMMON_STATE_UNKNOWN);
+    /**
+     * Internal class to map device states to corresponding postures.
+     *
+     * <p>This class encapsulates the logic for mapping device states to postures. The mapping is
+     * immutable after initialization to ensure thread safety.
+     */
+    private static class DeviceStateMapper {
+        /**
+         * Emulated device state
+         * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} to
+         * {@link CommonFoldingFeature.State} map.
+         *
+         * <p>This map must be immutable after initialization to ensure thread safety, as it may be
+         * accessed from multiple threads. Modifications should only occur during object
+         * construction.
+         */
+        private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
+
+        /**
+         * The list of device states that are supported.
+         *
+         * <p>This list must be immutable after initialization to ensure thread safety.
+         */
+        @NonNull
+        private final List<DeviceState> mSupportedStates;
+
+        final boolean mIsHalfOpenedSupported;
+
+        DeviceStateMapper(@NonNull Context context, @NonNull List<DeviceState> supportedStates) {
+            mSupportedStates = supportedStates;
+
+            final String[] deviceStatePosturePairs = context.getResources()
+                    .getStringArray(R.array.config_device_state_postures);
+            boolean isHalfOpenedSupported = false;
+            for (String deviceStatePosturePair : deviceStatePosturePairs) {
+                final String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
+                if (deviceStatePostureMapping.length != 2) {
+                    if (DEBUG) {
+                        Log.e(TAG, "Malformed device state posture pair: "
+                                + deviceStatePosturePair);
+                    }
+                    continue;
+                }
+
+                final int deviceState;
+                final int posture;
+                try {
+                    deviceState = Integer.parseInt(deviceStatePostureMapping[0]);
+                    posture = Integer.parseInt(deviceStatePostureMapping[1]);
+                } catch (NumberFormatException e) {
+                    if (DEBUG) {
+                        Log.e(TAG, "Failed to parse device state or posture: "
+                                        + deviceStatePosturePair,
+                                e);
+                    }
+                    continue;
+                }
+                isHalfOpenedSupported = isHalfOpenedSupported
+                        || posture == CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
+                mDeviceStateToPostureMap.put(deviceState, posture);
+            }
+            mIsHalfOpenedSupported = isHalfOpenedSupported;
         }
 
-        return posture;
+        boolean isDeviceStateToPostureMapEmpty() {
+            return mDeviceStateToPostureMap.size() == 0;
+        }
+
+        /**
+         * Validates if the provided deviceState exists in the {@link #mDeviceStateToPostureMap}
+         * which was initialized in the constructor of {@link DeviceStateMapper}.
+         * Returns a boolean value of whether the device state is valid.
+         */
+        boolean isDeviceStateValid(@NonNull DeviceState deviceState) {
+            // If the device state is not found in the map, indexOfKey returns a negative number.
+            return mDeviceStateToPostureMap.indexOfKey(deviceState.getIdentifier()) >= 0;
+        }
+
+        @CommonFoldingFeature.State
+        int getHingeState(@NonNull DeviceState deviceState) {
+            @CommonFoldingFeature.State
+            final int posture =
+                    mDeviceStateToPostureMap.get(deviceState.getIdentifier(), COMMON_STATE_UNKNOWN);
+            if (posture != CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
+                return posture;
+            }
+
+            final int baseStateIdentifier =
+                    DeviceStateUtil.calculateBaseStateIdentifier(deviceState, mSupportedStates);
+            return mDeviceStateToPostureMap.get(baseStateIdentifier, COMMON_STATE_UNKNOWN);
+        }
     }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 74cce68..dcc2d93 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -377,8 +377,16 @@
 
     @Nullable
     TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
-        return getContainer(container -> container.hasAppearedActivity(activityToken)
-                || container.hasPendingAppearedActivity(activityToken));
+        // When the new activity is launched to the topmost TF because the source activity
+        // was in that TF, and the source activity is finished before resolving the new activity,
+        // we will try to see if the new activity match a rule with the split activities below.
+        // If matched, it can be reparented.
+        final TaskFragmentContainer taskFragmentContainer
+                = getContainer(container -> container.hasPendingAppearedActivity(activityToken));
+        if (taskFragmentContainer != null) {
+            return taskFragmentContainer;
+        }
+        return getContainer(container -> container.hasAppearedActivity(activityToken));
     }
 
     @Nullable
diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
index bd430c0..09185ee 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp
+++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
@@ -29,6 +29,7 @@
 
     srcs: [
         "**/*.java",
+        "**/*.kt",
     ],
 
     static_libs: [
@@ -41,6 +42,7 @@
         "androidx.test.ext.junit",
         "flag-junit",
         "mockito-target-extended-minus-junit4",
+        "mockito-kotlin-nodeps",
         "truth",
         "testables",
         "platform-test-annotations",
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt
new file mode 100644
index 0000000..90887a7
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt
@@ -0,0 +1,341 @@
+/*
+ * 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 androidx.window.common
+
+import android.content.Context
+import android.content.res.Resources
+import android.hardware.devicestate.DeviceState
+import android.hardware.devicestate.DeviceStateManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.window.common.layout.CommonFoldingFeature
+import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_FLAT
+import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_HALF_OPENED
+import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_NO_FOLDING_FEATURES
+import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_UNKNOWN
+import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE
+import androidx.window.common.layout.DisplayFoldFeatureCommon
+import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATURE_PROPERTY_SUPPORTS_HALF_OPENED
+import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATURE_TYPE_SCREEN_FOLD_IN
+import com.android.internal.R
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+/**
+ * Test class for [DeviceStateManagerFoldingFeatureProducer].
+ *
+ * Build/Install/Run:
+ *  atest WMJetpackUnitTests:DeviceStateManagerFoldingFeatureProducerTest
+ */
+@RunWith(AndroidJUnit4::class)
+class DeviceStateManagerFoldingFeatureProducerTest {
+    @get:Rule
+    val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+    private val mMockDeviceStateManager = mock<DeviceStateManager>()
+    private val mMockResources = mock<Resources> {
+        on { getStringArray(R.array.config_device_state_postures) } doReturn DEVICE_STATE_POSTURES
+    }
+    private val mMockContext = mock<Context> {
+        on { resources } doReturn mMockResources
+    }
+    private val mRawFoldSupplier = mock<RawFoldingFeatureProducer> {
+        on { currentData } doReturn Optional.of(DISPLAY_FEATURES)
+        on { getData(any<Consumer<String>>()) } doAnswer { invocation ->
+            val callback = invocation.getArgument(0) as Consumer<String>
+            callback.accept(DISPLAY_FEATURES)
+        }
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_WLINFO_ONCREATE)
+    fun testRegisterCallback_whenWlinfoOncreateIsDisabled_usesMainExecutor() {
+        DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        verify(mMockDeviceStateManager).registerCallback(eq(mMockContext.mainExecutor), any())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_WLINFO_ONCREATE)
+    fun testRegisterCallback_whenWlinfoOncreateIsEnabled_usesRunnableRun() {
+        val executorCaptor = ArgumentCaptor.forClass(Executor::class.java)
+        val runnable = mock<Runnable>()
+
+        DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        verify(mMockDeviceStateManager).registerCallback(executorCaptor.capture(), any())
+        executorCaptor.value.execute(runnable)
+        verify(runnable).run()
+    }
+
+    @Test
+    fun testGetCurrentData_validCurrentState_returnsFoldingFeatureWithState() {
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+        ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+
+        val currentData = ffp.getCurrentData()
+
+        assertThat(currentData).isPresent()
+        assertThat(currentData.get()).containsExactlyElementsIn(HALF_OPENED_FOLDING_FEATURES)
+    }
+
+    @Test
+    fun testGetCurrentData_invalidCurrentState_returnsEmptyOptionalFoldingFeature() {
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        val currentData = ffp.getCurrentData()
+
+        assertThat(currentData).isEmpty()
+    }
+
+    @Test
+    fun testGetFoldsWithUnknownState_validFoldingFeature_returnsFoldingFeaturesWithUnknownState() {
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        val result = ffp.getFoldsWithUnknownState()
+
+        assertThat(result).containsExactlyElementsIn(UNKNOWN_STATE_FOLDING_FEATURES)
+    }
+
+    @Test
+    fun testGetFoldsWithUnknownState_emptyFoldingFeature_returnsEmptyList() {
+        mRawFoldSupplier.stub {
+            on { currentData } doReturn Optional.empty()
+        }
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        val result = ffp.getFoldsWithUnknownState()
+
+        assertThat(result).isEmpty()
+    }
+
+    @Test
+    fun testGetDisplayFeatures_validFoldingFeature_returnsDisplayFoldFeatures() {
+        mRawFoldSupplier.stub {
+            on { currentData } doReturn Optional.of(DISPLAY_FEATURES_HALF_OPENED_HINGE)
+        }
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        val result = ffp.displayFeatures
+
+        assertThat(result).containsExactly(
+            DisplayFoldFeatureCommon(
+                DISPLAY_FOLD_FEATURE_TYPE_SCREEN_FOLD_IN,
+                setOf(DISPLAY_FOLD_FEATURE_PROPERTY_SUPPORTS_HALF_OPENED),
+            ),
+        )
+    }
+
+    @Test
+    fun testIsHalfOpenedSupported_withHalfOpenedPostures_returnsTrue() {
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        assertThat(ffp.isHalfOpenedSupported).isTrue()
+    }
+
+    @Test
+    fun testIsHalfOpenedSupported_withEmptyPostures_returnsFalse() {
+        mMockResources.stub {
+            on { getStringArray(R.array.config_device_state_postures) } doReturn emptyArray()
+        }
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        assertThat(ffp.isHalfOpenedSupported).isFalse()
+    }
+
+    @Test
+    fun testGetData_emptyDisplayFeaturesString_callsConsumerWithEmptyList() {
+        mRawFoldSupplier.stub {
+            on { getData(any<Consumer<String>>()) } doAnswer { invocation ->
+                val callback = invocation.getArgument(0) as Consumer<String>
+                callback.accept("")
+            }
+        }
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+        val storeFeaturesConsumer = mock<Consumer<List<CommonFoldingFeature>>>()
+
+        ffp.getData(storeFeaturesConsumer)
+
+        verify(storeFeaturesConsumer).accept(emptyList())
+    }
+
+    @Test
+    fun testGetData_validState_callsConsumerWithFoldingFeatures() {
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+        ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+        val storeFeaturesConsumer = mock<Consumer<List<CommonFoldingFeature>>>()
+
+        ffp.getData(storeFeaturesConsumer)
+
+        verify(storeFeaturesConsumer).accept(HALF_OPENED_FOLDING_FEATURES)
+    }
+
+    @Test
+    fun testGetData_invalidState_addsAcceptOnceConsumerToDataChangedCallback() {
+        val ffp = DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+        val storeFeaturesConsumer = mock<Consumer<List<CommonFoldingFeature>>>()
+
+        ffp.getData(storeFeaturesConsumer)
+
+        verify(storeFeaturesConsumer, never()).accept(any())
+        ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_HALF_OPENED)
+        ffp.mDeviceStateCallback.onDeviceStateChanged(DEVICE_STATE_OPENED)
+        verify(storeFeaturesConsumer).accept(HALF_OPENED_FOLDING_FEATURES)
+    }
+
+    @Test
+    fun testDeviceStateMapper_malformedDeviceStatePosturePair_skipsPair() {
+        val malformedDeviceStatePostures = arrayOf(
+            // Missing the posture.
+            "0",
+            // Empty string.
+            "",
+            // Too many elements.
+            "0:1:2",
+        )
+        mMockResources.stub {
+            on { getStringArray(R.array.config_device_state_postures) } doReturn
+                    malformedDeviceStatePostures
+        }
+
+        DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        verify(mMockDeviceStateManager, never()).registerCallback(any(), any())
+    }
+
+    @Test
+    fun testDeviceStateMapper_invalidNumberFormat_skipsPair() {
+        val invalidNumberFormatDeviceStatePostures = arrayOf("a:1", "0:b", "a:b", ":1")
+        mMockResources.stub {
+            on { getStringArray(R.array.config_device_state_postures) } doReturn
+                    invalidNumberFormatDeviceStatePostures
+        }
+
+        DeviceStateManagerFoldingFeatureProducer(
+            mMockContext,
+            mRawFoldSupplier,
+            mMockDeviceStateManager,
+        )
+
+        verify(mMockDeviceStateManager, never()).registerCallback(any(), any())
+    }
+
+    companion object {
+        // Supported device states configuration.
+        private enum class SupportedDeviceStates {
+            CLOSED, HALF_OPENED, OPENED, REAR_DISPLAY, CONCURRENT;
+
+            override fun toString() = ordinal.toString()
+
+            fun toDeviceState(): DeviceState =
+                DeviceState(DeviceState.Configuration.Builder(ordinal, name).build())
+        }
+
+        // Map of supported device states supplied by DeviceStateManager to WM Jetpack posture.
+        private val DEVICE_STATE_POSTURES =
+            arrayOf(
+                "${SupportedDeviceStates.CLOSED}:$COMMON_STATE_NO_FOLDING_FEATURES",
+                "${SupportedDeviceStates.HALF_OPENED}:$COMMON_STATE_HALF_OPENED",
+                "${SupportedDeviceStates.OPENED}:$COMMON_STATE_FLAT",
+                "${SupportedDeviceStates.REAR_DISPLAY}:$COMMON_STATE_NO_FOLDING_FEATURES",
+                "${SupportedDeviceStates.CONCURRENT}:$COMMON_STATE_USE_BASE_STATE",
+            )
+        private val DEVICE_STATE_HALF_OPENED = SupportedDeviceStates.HALF_OPENED.toDeviceState()
+        private val DEVICE_STATE_OPENED = SupportedDeviceStates.OPENED.toDeviceState()
+
+        // WindowsManager Jetpack display features.
+        private val DISPLAY_FEATURES = "fold-[1104,0,1104,1848]"
+        private val DISPLAY_FEATURES_HALF_OPENED_HINGE = "$DISPLAY_FEATURES-half-opened"
+        private val HALF_OPENED_FOLDING_FEATURES = CommonFoldingFeature.parseListFromString(
+            DISPLAY_FEATURES,
+            COMMON_STATE_HALF_OPENED,
+        )
+        private val UNKNOWN_STATE_FOLDING_FEATURES = CommonFoldingFeature.parseListFromString(
+            DISPLAY_FEATURES,
+            COMMON_STATE_UNKNOWN,
+        )
+    }
+}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 7fab371..bc4916a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -535,7 +535,8 @@
         // container1.
         container2.setInfo(mTransaction, mInfo);
 
-        assertTrue(container2.hasActivity(mActivity.getActivityToken()));
+        assertTrue(container1.hasActivity(mActivity.getActivityToken()));
+        assertFalse(container2.hasActivity(mActivity.getActivityToken()));
         // When the pending appeared record is removed from container1, we respect the appeared
         // record in container2.
         container1.removePendingAppearedActivity(mActivity.getActivityToken());
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 94809f2..f857429 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -147,8 +147,10 @@
 java_library {
     name: "WindowManager-Shell-lite-proto",
 
-    srcs: ["src/com/android/wm/shell/desktopmode/education/data/proto/**/*.proto"],
-
+    srcs: [
+        "src/com/android/wm/shell/desktopmode/education/data/proto/**/*.proto",
+        "src/com/android/wm/shell/desktopmode/persistence/*.proto",
+    ],
     proto: {
         type: "lite",
     },
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 526ccd5..63a2880 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -122,6 +122,16 @@
 }
 
 flag {
+    name: "enable_shell_top_task_tracking"
+    namespace: "multitasking"
+    description: "Enables tracking top tasks from the shell"
+    bug: "342627272"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_bubble_bar_in_persistent_task_bar"
     namespace: "multitasking"
     description: "Enable bubble bar to be shown in the persistent task bar"
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
index 991cdcf..c7b4c65 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
index 991cdcf..c7b4c65 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml b/libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml
new file mode 100644
index 0000000..07e5ac1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/textColorTertiary"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/system_on_tertiary_fixed"
+        android:pathData="M419,880Q391,880 366.5,868Q342,856 325,834L107,557L126,537Q146,516 174,512Q202,508 226,523L300,568L300,240Q300,223 311.5,211.5Q323,200 340,200Q357,200 369,211.5Q381,223 381,240L381,712L284,652L388,785Q394,792 402,796Q410,800 419,800L640,800Q673,800 696.5,776.5Q720,753 720,720L720,560Q720,543 708.5,531.5Q697,520 680,520L461,520L461,440L680,440Q730,440 765,475Q800,510 800,560L800,720Q800,786 753,833Q706,880 640,880L419,880ZM167,340Q154,318 147,292.5Q140,267 140,240Q140,157 198.5,98.5Q257,40 340,40Q423,40 481.5,98.5Q540,157 540,240Q540,267 533,292.5Q526,318 513,340L444,300Q452,286 456,271.5Q460,257 460,240Q460,190 425,155Q390,120 340,120Q290,120 255,155Q220,190 220,240Q220,257 224,271.5Q228,286 236,300L167,340ZM502,620L502,620L502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620L502,620Z" />
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml
new file mode 100644
index 0000000..a12a746
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="30dp" />
+            <solid android:color="@android:color/system_tertiary_fixed" />
+        </shape>
+    </item>
+</layer-list>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_left_arrow.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_left_arrow.xml
new file mode 100644
index 0000000..aadffb5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_left_arrow.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- An arrow that points towards left. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="10dp"
+    android:height="12dp"
+    android:viewportWidth="10"
+    android:viewportHeight="12">
+  <path
+      android:pathData="M2.858,4.285C1.564,5.062 1.564,6.938 2.858,7.715L10,12L10,0L2.858,4.285Z"
+      android:fillColor="@android:color/system_tertiary_fixed"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_top_arrow.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_top_arrow.xml
new file mode 100644
index 0000000..e3c9a66
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_tooltip_top_arrow.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.
+  -->
+
+<!-- An arrow that points upwards. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="12dp"
+    android:height="9dp"
+    android:viewportWidth="12"
+    android:viewportHeight="9">
+  <path
+      android:pathData="M7.715,1.858C6.938,0.564 5.062,0.564 4.285,1.858L0,9L12,9L7.715,1.858Z"
+      android:fillColor="@android:color/system_tertiary_fixed"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_handle.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_handle.xml
index c0ff192..1d1cdfa 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_handle.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_handle.xml
@@ -28,6 +28,8 @@
         android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
         android:paddingVertical="16dp"
         android:paddingHorizontal="10dp"
+        android:screenReaderFocusable="true"
+        android:importantForAccessibility="yes"
         android:contentDescription="@string/handle_text"
         android:src="@drawable/decor_handle_dark"
         tools:tint="@color/desktop_mode_caption_handle_bar_dark"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
index 7dcb3c2..3dbf754 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
@@ -31,14 +31,16 @@
         android:orientation="horizontal"
         android:clickable="true"
         android:focusable="true"
+        android:contentDescription="@string/desktop_mode_app_header_chip_text"
         android:layout_marginStart="12dp">
         <ImageView
             android:id="@+id/application_icon"
             android:layout_width="@dimen/desktop_mode_caption_icon_radius"
             android:layout_height="@dimen/desktop_mode_caption_icon_radius"
             android:layout_gravity="center_vertical"
-            android:contentDescription="@string/app_icon_text"
             android:layout_marginStart="6dp"
+            android:clickable="false"
+            android:focusable="false"
             android:scaleType="centerCrop"/>
 
         <TextView
@@ -53,18 +55,22 @@
             android:layout_gravity="center_vertical"
             android:layout_weight="1"
             android:layout_marginStart="8dp"
+            android:clickable="false"
+            android:focusable="false"
             tools:text="Gmail"/>
 
         <ImageButton
             android:id="@+id/expand_menu_button"
             android:layout_width="16dp"
             android:layout_height="16dp"
-            android:contentDescription="@string/expand_menu_text"
             android:src="@drawable/ic_baseline_expand_more_24"
             android:background="@null"
             android:scaleType="fitCenter"
             android:clickable="false"
             android:focusable="false"
+            android:screenReaderFocusable="false"
+            android:importantForAccessibility="no"
+            android:contentDescription="@null"
             android:layout_marginHorizontal="8dp"
             android:layout_gravity="center_vertical"/>
 
@@ -90,6 +96,7 @@
 
     <com.android.wm.shell.windowdecor.MaximizeButtonView
         android:id="@+id/maximize_button_view"
+        android:importantForAccessibility="no"
         android:layout_width="44dp"
         android:layout_height="40dp"
         android:layout_gravity="end"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index 64f71c7..6913e54 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -43,13 +43,15 @@
             android:layout_height="@dimen/desktop_mode_caption_icon_radius"
             android:layout_marginStart="12dp"
             android:layout_marginEnd="12dp"
-            android:contentDescription="@string/app_icon_text"/>
+            android:contentDescription="@string/app_icon_text"
+            android:importantForAccessibility="no"/>
 
         <TextView
             android:id="@+id/application_name"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             tools:text="Gmail"
+            android:importantForAccessibility="no"
             android:textColor="?androidprv:attr/materialColorOnSurface"
             android:textSize="14sp"
             android:textFontWeight="500"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 5fe3f2a..35ef239 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -41,6 +41,8 @@
                 android:id="@+id/maximize_menu_maximize_button"
                 style="?android:attr/buttonBarButtonStyle"
                 android:stateListAnimator="@null"
+                android:importantForAccessibility="yes"
+                android:contentDescription="@string/desktop_mode_maximize_menu_maximize_button_text"
                 android:layout_marginRight="8dp"
                 android:layout_marginBottom="4dp"
                 android:alpha="0"/>
@@ -53,6 +55,7 @@
                 android:layout_marginBottom="76dp"
                 android:gravity="center"
                 android:fontFamily="google-sans-text"
+                android:importantForAccessibility="no"
                 android:text="@string/desktop_mode_maximize_menu_maximize_text"
                 android:textColor="?androidprv:attr/materialColorOnSurface"
                 android:alpha="0"/>
@@ -78,6 +81,8 @@
                     android:layout_height="@dimen/desktop_mode_maximize_menu_button_height"
                     android:layout_marginRight="4dp"
                     android:background="@drawable/desktop_mode_maximize_menu_button_background"
+                    android:importantForAccessibility="yes"
+                    android:contentDescription="@string/desktop_mode_maximize_menu_snap_left_button_text"
                     android:stateListAnimator="@null"/>
 
                 <Button
@@ -86,6 +91,8 @@
                     android:layout_width="41dp"
                     android:layout_height="@dimen/desktop_mode_maximize_menu_button_height"
                     android:background="@drawable/desktop_mode_maximize_menu_button_background"
+                    android:importantForAccessibility="yes"
+                    android:contentDescription="@string/desktop_mode_maximize_menu_snap_right_button_text"
                     android:stateListAnimator="@null"/>
             </LinearLayout>
             <TextView
@@ -96,6 +103,7 @@
                 android:layout_marginBottom="76dp"
                 android:layout_gravity="center"
                 android:gravity="center"
+                android:importantForAccessibility="no"
                 android:fontFamily="google-sans-text"
                 android:text="@string/desktop_mode_maximize_menu_snap_text"
                 android:textColor="?androidprv:attr/materialColorOnSurface"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml
new file mode 100644
index 0000000..a269b9e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_left_arrow_tooltip.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:elevation="1dp"
+    android:orientation="horizontal">
+
+    <!-- ImageView for the arrow icon, positioned horizontally at the start of the tooltip
+    container. -->
+    <ImageView
+        android:id="@+id/arrow_icon"
+        android:layout_width="10dp"
+        android:layout_height="12dp"
+        android:layout_gravity="center_vertical"
+        android:src="@drawable/desktop_windowing_education_tooltip_left_arrow" />
+
+    <!-- Layout for the tooltip, excluding the arrow. Separating the tooltip content from the arrow
+    allows scaling of only the tooltip container when the content changes, without affecting the
+    arrow. -->
+    <include layout="@layout/desktop_windowing_education_tooltip_container" />
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml
new file mode 100644
index 0000000..bdee883
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tooltip_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/desktop_windowing_education_tooltip_background"
+    android:orientation="horizontal"
+    android:padding="@dimen/desktop_windowing_education_tooltip_padding">
+
+    <ImageView
+        android:id="@+id/tooltip_icon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_gravity="center_vertical"
+        android:src="@drawable/app_handle_education_tooltip_icon" />
+
+    <TextView
+        android:id="@+id/tooltip_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="2dp"
+        android:lineHeight="20dp"
+        android:maxWidth="150dp"
+        android:textColor="@android:color/system_on_tertiary_fixed"
+        android:textFontWeight="500"
+        android:textSize="14sp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml
new file mode 100644
index 0000000..c73c1da
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_top_arrow_tooltip.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:elevation="1dp"
+    android:orientation="vertical">
+
+    <!-- ImageView for the arrow icon, positioned vertically above the tooltip container. -->
+    <ImageView
+        android:id="@+id/arrow_icon"
+        android:layout_width="12dp"
+        android:layout_height="9dp"
+        android:layout_gravity="center_horizontal"
+        android:src="@drawable/desktop_windowing_education_tooltip_top_arrow" />
+
+    <!-- Layout for the tooltip, excluding the arrow. Separating the tooltip content from the arrow
+    allows scaling of only the tooltip container when the content changes, without affecting the
+    arrow. -->
+    <include layout="@layout/desktop_windowing_education_tooltip_container" />
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
index 045b975..462a49c 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
@@ -99,11 +99,11 @@
 
                 </LinearLayout>
 
-                <FrameLayout
+
+                <LinearLayout
                     android:minHeight="@dimen/letterbox_restart_dialog_button_height"
-                    android:layout_width="match_parent"
+                    android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    style="?android:attr/buttonBarButtonStyle"
                     android:layout_gravity="end">
 
                     <Button
@@ -133,7 +133,7 @@
                         android:text="@string/letterbox_restart_restart"
                         android:contentDescription="@string/letterbox_restart_restart"/>
 
-                </FrameLayout>
+                </LinearLayout>
 
             </LinearLayout>
 
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index cf1b894..b734d2d 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -19,7 +19,8 @@
 
     <FrameLayout
         android:layout_width="44dp"
-        android:layout_height="40dp">
+        android:layout_height="40dp"
+        android:importantForAccessibility="noHideDescendants">
         <ProgressBar
             android:id="@+id/progress_bar"
             style="?android:attr/progressBarStyleHorizontal"
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 4dbff34..50aa4ca 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Skermskoot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Maak in blaaier oop"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nuwe venster"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Bestuur vensters"</string>
     <string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Maak kieslys oop"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hierdie app se grootte kan nie verander word nie"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index d70a317..281e313 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ቅጽበታዊ ገፅ ዕይታ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"በአሳሽ ውስጥ ክፈት"</string>
     <string name="new_window_text" msgid="6318648868380652280">"አዲስ መስኮት"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"መስኮቶችን አስተዳድር"</string>
     <string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ይህ መተግበሪያ መጠኑ ሊቀየር አይችልም"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index cb316e9..2c0b34a 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"لقطة شاشة"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"فتح في المتصفِّح"</string>
     <string name="new_window_text" msgid="6318648868380652280">"نافذة جديدة"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"إدارة النوافذ"</string>
     <string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"فتح القائمة"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"لا يمكن تغيير حجم نافذة هذا التطبيق"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 9f7fa7c..c5e3716 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"স্ক্ৰীনশ্বট"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ব্ৰাউজাৰত খোলক"</string>
     <string name="new_window_text" msgid="6318648868380652280">"নতুন ৱিণ্ড’"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ৱিণ্ড’ পৰিচালনা কৰক"</string>
     <string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খোলক"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই এপ্‌টোৰ আকাৰ সলনি কৰিব নোৱাৰি"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 90962f0..e23e8d0 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Skrinşot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerdə açın"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Yeni pəncərə"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Pəncərələri idarə edin"</string>
     <string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Menyunu açın"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu tətbiqin ölçüsünü dəyişmək olmur"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 9c6ed6b..283d1f5 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Snimak ekrana"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvorite u pregledaču"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Upravljajte prozorima"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Otvorite meni"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veličina ove aplikacije ne može da se promeni"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index e8b24bd..48914f1 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Здымак экрана"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Адкрыць у браўзеры"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Новае акно"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Кіраваць вокнамі"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Адкрыць меню"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Немагчыма змяніць памер праграмы"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 1f188f6..b385d28 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Екранна снимка"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Отваряне в браузър"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Управление на прозорците"</string>
     <string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Отваряне на менюто"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Това приложение не може да бъде преоразмерено"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index b572038..56fcf7f 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"স্ক্রিনশট"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ব্রাউজারে খুলুন"</string>
     <string name="new_window_text" msgid="6318648868380652280">"নতুন উইন্ডো"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"উইন্ডো ম্যানেজ করুন"</string>
     <string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খুলুন"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই অ্যাপ ছোট বড় করা যাবে না"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 630b31b..95362bb 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Snimak ekrana"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvaranje u pregledniku"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje menija"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 98ec381..e0b2538 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Obre al navegador"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Finestra nova"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gestiona les finestres"</string>
     <string name="close_text" msgid="4986518933445178928">"Tanca"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Obre el menú"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No es pot canviar la mida d\'aquesta aplicació"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 08d5bb5..3aae52e 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Snímek obrazovky"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otevřít v prohlížeči"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Spravovat okna"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Otevřít nabídku"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikost aplikace nelze změnit"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index ae1bb9a..fdc5913 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Åbn i browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nyt vindue"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduer"</string>
     <string name="close_text" msgid="4986518933445178928">"Luk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Åbn menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Størrelsen på denne app kan ikke justeres"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index abbfa66..913e3d0 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Im Browser öffnen"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Neues Fenster"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Fenster verwalten"</string>
     <string name="close_text" msgid="4986518933445178928">"Schließen"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Menü öffnen"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Die Größe dieser App kann nicht geändert werden"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 0f762d3..564fa91 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Στιγμιότυπο οθόνης"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Άνοιγμα σε πρόγραμμα περιήγησης"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Νέο παράθυρο"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Διαχείριση παραθύρων"</string>
     <string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Άνοιγμα μενού"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Δεν είναι δυνατή η αλλαγή μεγέθους αυτής της εφαρμογής"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 2314e6b..e4ece93 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"New window"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index f5b0a27..14fe60b 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"New Window"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Manage Windows"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Open Menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 2314e6b..e4ece93 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"New window"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 2314e6b..e4ece93 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"New window"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 6292be5..dda97ab 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎Screenshot‎‏‎‎‏‎"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎Open in browser‎‏‎‎‏‎"</string>
     <string name="new_window_text" msgid="6318648868380652280">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎New Window‎‏‎‎‏‎"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎Manage Windows‎‏‎‎‏‎"</string>
     <string name="close_text" msgid="4986518933445178928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎Close‎‏‎‎‏‎"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎Close Menu‎‏‎‎‏‎"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎Open Menu‎‏‎‎‏‎"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‏‎Maximize Screen‎‏‎‎‏‎"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎Snap Screen‎‏‎‎‏‎"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎This app can\'t be resized‎‏‎‎‏‎"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 0dd0a99..a4c0363 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nueva ventana"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Administrar ventanas"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Abrir el menú"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta app"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 6df154d..69f7d7b 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Ventana nueva"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gestionar ventanas"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta aplicación"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index cfaa0d3..3d929f6 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Ekraanipilt"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Avamine brauseris"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Uus aken"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Akende haldamine"</string>
     <string name="close_text" msgid="4986518933445178928">"Sule"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Ava menüü"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Selle rakenduse aknasuurust ei saa muuta"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 509c97e..39bcf08 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Pantaila-argazkia"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Ireki arakatzailean"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Leiho berria"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Kudeatu leihoak"</string>
     <string name="close_text" msgid="4986518933445178928">"Itxi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Ireki menua"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezin zaio aldatu tamaina aplikazio honi"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 223b671..9f607bf 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"نماگرفت"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"باز کردن در مرورگر"</string>
     <string name="new_window_text" msgid="6318648868380652280">"پنجره جدید"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"مدیریت کردن پنجره‌ها"</string>
     <string name="close_text" msgid="4986518933445178928">"بستن"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"باز کردن منو"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اندازه این برنامه را نمی‌توان تغییر داد"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 9083c4d..04b6241 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Kuvakaappaus"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Avaa selaimessa"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Uusi ikkuna"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Hallinnoi ikkunoita"</string>
     <string name="close_text" msgid="4986518933445178928">"Sulje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Avaa valikko"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Tämän sovellusikkunan kokoa ei voi muuttaa"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 2f284ad..7312952 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Capture d\'écran"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans le navigateur"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 92f579d..8f4e58f 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Capture d\'écran"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans un navigateur"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 5126aa2..5c0aa07 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Ventá nova"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Xestionar as ventás"</string>
     <string name="close_text" msgid="4986518933445178928">"Pechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Non se pode cambiar o tamaño desta aplicación"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 3418637..a382d0b 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"સ્ક્રીનશૉટ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"બ્રાઉઝરમાં ખોલો"</string>
     <string name="new_window_text" msgid="6318648868380652280">"નવી વિન્ડો"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"વિન્ડો મેનેજ કરો"</string>
     <string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"મેનૂ ખોલો"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"આ ઍપના કદમાં વધઘટ કરી શકાતો નથી"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 8eaa86f..d6fc85b 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"स्क्रीनशॉट"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउज़र में खोलें"</string>
     <string name="new_window_text" msgid="6318648868380652280">"नई विंडो"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"विंडो मैनेज करें"</string>
     <string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"मेन्यू खोलें"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"इस ऐप्लिकेशन का साइज़ नहीं बदला जा सकता"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 5427a9b..f233c02 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Snimka zaslona"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvori u pregledniku"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje izbornika"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 5b337ea..aaa4e26 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Képernyőkép"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Megnyitás böngészőben"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Új ablak"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Ablakok kezelése"</string>
     <string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Menü megnyitása"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezt az alkalmazást nem lehet átméretezni"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index ef38307..cd21faa 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Սքրինշոթ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Բացել դիտարկիչում"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Նոր պատուհան"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Կառավարել պատուհանները"</string>
     <string name="close_text" msgid="4986518933445178928">"Փակել"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Բացել ընտրացանկը"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Այս հավելվածի չափը հնարավոր չէ փոխել"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index fcb3e72..ba0683d 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Buka di browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Jendela Baru"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Kelola Jendela"</string>
     <string name="close_text" msgid="4986518933445178928">"Tutup"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ukuran aplikasi ini tidak dapat diubah"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 9755083..b427eaf 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Skjámynd"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Opna í vafra"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nýr gluggi"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Stjórna gluggum"</string>
     <string name="close_text" msgid="4986518933445178928">"Loka"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Opna valmynd"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ekki er hægt að breyta stærð þessa forrits"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 5f9c492..9eb9d4e 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Apri nel browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nuova finestra"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gestisci finestre"</string>
     <string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Apri menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Non è possibile ridimensionare questa app"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index ddbb89a..3e82198 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"צילום מסך"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"פתיחה בדפדפן"</string>
     <string name="new_window_text" msgid="6318648868380652280">"חלון חדש"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ניהול החלונות"</string>
     <string name="close_text" msgid="4986518933445178928">"סגירה"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"פתיחת התפריט"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"לא ניתן לשנות את גודל החלון של האפליקציה הזו"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 8284837..5d8dc5e 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"スクリーンショット"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ブラウザで開く"</string>
     <string name="new_window_text" msgid="6318648868380652280">"新しいウィンドウ"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ウィンドウを管理する"</string>
     <string name="close_text" msgid="4986518933445178928">"閉じる"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"メニューを開く"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"このアプリはサイズ変更できません"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 82828d8..de08b9c 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ეკრანის ანაბეჭდი"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ბრაუზერში გახსნა"</string>
     <string name="new_window_text" msgid="6318648868380652280">"ახალი ფანჯარა"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ფანჯრების მართვა"</string>
     <string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"მენიუს გახსნა"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"აპის ზომის შეცვლა შეუძლებელია"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index af4e4f3..dd4e5c9 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Скриншот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Браузерден ашу"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Жаңа терезе"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Терезелерді басқару"</string>
     <string name="close_text" msgid="4986518933445178928">"Жабу"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Мәзірді ашу"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бұл қолданбаның өлшемі өзгертілмейді."</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index c3a3800..96fe62e 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"រូបថតអេក្រង់"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"បើកក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
     <string name="new_window_text" msgid="6318648868380652280">"វិនដូ​ថ្មី"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"គ្រប់គ្រង​វិនដូ"</string>
     <string name="close_text" msgid="4986518933445178928">"បិទ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"បិទ​ម៉ឺនុយ"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"បើកម៉ឺនុយ"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"មិនអាចប្ដូរទំហំ​កម្មវិធីនេះ​បានទេ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index aa8cec5..b38f744 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -34,8 +34,8 @@
     <string name="dock_forced_resizable" msgid="7429086980048964687">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು"</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="2733543750291266047">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ಆ್ಯಪ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
     <string name="dock_multi_instances_not_supported_text" msgid="5011042177901502928">"ಈ ಆ್ಯಪ್ ಅನ್ನು 1 ವಿಂಡೋದಲ್ಲಿ ಮಾತ್ರ ತೆರೆಯಬಹುದು"</string>
-    <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
+    <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಆ್ಯಪ್ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
+    <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಆ್ಯಪ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
     <string name="accessibility_divider" msgid="6407584574218956849">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string>
     <string name="divider_title" msgid="1963391955593749442">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string>
     <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ಬ್ರೌಸರ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ"</string>
     <string name="new_window_text" msgid="6318648868380652280">"ಹೊಸ ವಿಂಡೋ"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ವಿಂಡೋಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
     <string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"ಮೆನು ತೆರೆಯಿರಿ"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index fc2a1b9..421d466 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"스크린샷"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"브라우저에서 열기"</string>
     <string name="new_window_text" msgid="6318648868380652280">"새 창"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"창 관리"</string>
     <string name="close_text" msgid="4986518933445178928">"닫기"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"메뉴 열기"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"이 앱은 크기를 조절할 수 없습니다."</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index c294725..abafd7a 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Скриншот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Серепчиден ачуу"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Жаңы терезе"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Терезелерди тескөө"</string>
     <string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Менюну ачуу"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бул колдонмонун өлчөмүн өзгөртүүгө болбойт"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 7d2f999..5bc5316 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ຮູບໜ້າຈໍ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ເປີດໃນໂປຣແກຣມທ່ອງເວັບ"</string>
     <string name="new_window_text" msgid="6318648868380652280">"ໜ້າຈໍໃໝ່"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ຈັດການໜ້າຈໍ"</string>
     <string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"ເປີດເມນູ"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ບໍ່ສາມາດປັບຂະໜາດແອັບນີ້ໄດ້"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index be446a6..01a72ee 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Ekrano kopija"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Atidaryti naršyklėje"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Naujas langas"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Tvarkyti langus"</string>
     <string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Atidaryti meniu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Negalima keisti šios programos dydžio"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index ed0c05e..2f235ba 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Ekrānuzņēmums"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Atvērt pārlūkā"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Jauns logs"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Pārvaldīt logus"</string>
     <string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Atvērt izvēlni"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Šīs lietotnes loga lielumu nevar mainīt."</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 9b24b7f..e58d8fc 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Слика од екранот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Отвори во прелистувач"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Управувајте со прозорци"</string>
     <string name="close_text" msgid="4986518933445178928">"Затворете"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Отвори го менито"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Не може да се промени големината на апликацијава"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index ac67f8d..d51c3fb 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"സ്ക്രീൻഷോട്ട്"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ബ്രൗസറിൽ തുറക്കുക"</string>
     <string name="new_window_text" msgid="6318648868380652280">"പുതിയ വിന്‍ഡോ"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"വിൻഡോകൾ മാനേജ് ചെയ്യുക"</string>
     <string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"മെനു തുറക്കുക"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്‌ക്രീൻ വലുതാക്കുക"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്‌ക്രീൻ സ്‌നാപ്പ് ചെയ്യുക"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ഈ ആപ്പിന്റെ വലുപ്പം മാറ്റാനാകില്ല"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 6d5deb3..f7e6a6c 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Дэлгэцийн агшин"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Хөтчид нээх"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Шинэ цонх"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Windows-г удирдах"</string>
     <string name="close_text" msgid="4986518933445178928">"Хаах"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Цэс нээх"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Энэ аппын хэмжээг өөрчлөх боломжгүй"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 49747f2..4832284 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"स्क्रीनशॉट"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउझरमध्ये उघडा"</string>
     <string name="new_window_text" msgid="6318648868380652280">"नवीन विंडो"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"विंडो व्यवस्थापित करा"</string>
     <string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"मेनू उघडा"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"या अ‍ॅपचा आकार बदलला जाऊ शकत नाही"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index dec3893..e510d72 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Tangkapan skrin"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Buka dalam penyemak imbas"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Tetingkap Baharu"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Urus Tetingkap"</string>
     <string name="close_text" msgid="4986518933445178928">"Tutup"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Apl ini tidak boleh diubah saiz"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 908ef81..aac6f84 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ဘရောင်ဇာတွင် ဖွင့်ရန်"</string>
     <string name="new_window_text" msgid="6318648868380652280">"ဝင်းဒိုးအသစ်"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ဝင်းဒိုးများ စီမံရန်"</string>
     <string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"မီနူး ဖွင့်ရန်"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ဤအက်ပ်ကို အရွယ်ပြင်၍ မရပါ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 01ca4ed..175133d 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Skjermbilde"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Åpne i nettleseren"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nytt vindu"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduene"</string>
     <string name="close_text" msgid="4986518933445178928">"Lukk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Åpne menyen"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Du kan ikke endre størrelse på denne appen"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 05ce071..d3a7e12 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"स्क्रिनसट"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउजरमा खोल्नुहोस्"</string>
     <string name="new_window_text" msgid="6318648868380652280">"नयाँ विन्डो"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"विन्डोहरू व्यवस्थापन गर्नुहोस्"</string>
     <string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"मेनु खोल्नुहोस्"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"यो एपको आकार बदल्न मिल्दैन"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 9ec4444..747afe3 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Openen in browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nieuw venster"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Vensters beheren"</string>
     <string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Menu openen"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Het formaat van deze app kan niet worden aangepast"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 7ee7342..a5adcbe 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ସ୍କ୍ରିନସଟ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ବ୍ରାଉଜରରେ ଖୋଲନ୍ତୁ"</string>
     <string name="new_window_text" msgid="6318648868380652280">"ନୂଆ ୱିଣ୍ଡୋ"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
     <string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"ମେନୁ ଖୋଲନ୍ତୁ"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ଏହି ଆପକୁ ରିସାଇଜ କରାଯାଇପାରିବ ନାହିଁ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index cc31e3c..5a4e61a 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲ੍ਹੋ"</string>
     <string name="new_window_text" msgid="6318648868380652280">"ਨਵੀਂ ਵਿੰਡੋ"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"ਵਿੰਡੋਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ਇਸ ਐਪ ਦਾ ਆਕਾਰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 5dd14c9..a14bb99 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Zrzut ekranu"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otwórz w przeglądarce"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nowe okno"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Zarządzaj oknami"</string>
     <string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Otwórz menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nie można zmienić rozmiaru tej aplikacji"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index d9c3d44..75c445c 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de tela"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 1ace699..1d31d0d 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de ecrã"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Faça a gestão das janelas"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar esta app"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index d9c3d44..75c445c 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de tela"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index ffaea97..3c763ea 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Captură de ecran"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Deschide în browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Fereastră nouă"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Gestionează ferestrele"</string>
     <string name="close_text" msgid="4986518933445178928">"Închide"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Deschide meniul"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Aplicația nu poate fi redimensionată"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 6231e3e..a6c20ad 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Скриншот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Открыть в браузере"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Новое окно"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Управление окнами"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Открыть меню"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Изменить размер приложения нельзя."</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 824bd8d..ed76269 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"තිර රුව"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"බ්‍රව්සරයේ විවෘත කරන්න"</string>
     <string name="new_window_text" msgid="6318648868380652280">"නව කවුළුව"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"කවුළු කළමනාකරණය කරන්න"</string>
     <string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"මෙනුව විවෘත කරන්න"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"මෙම යෙදුම ප්‍රතිප්‍රමාණ කළ නොහැක"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 4a1508d..529a693 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Snímka obrazovky"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvoriť v prehliadači"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Správa okien"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Otvoriť ponuku"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veľkosť tejto aplikácie sa nedá zmeniť"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index dd2f9f0..fe08e04 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Posnetek zaslona"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Odpri v brskalniku"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Novo okno"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje oken"</string>
     <string name="close_text" msgid="4986518933445178928">"Zapri"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Odpri meni"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikosti te aplikacije ni mogoče spremeniti"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 322525b..a4a7e74 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Pamja e ekranit"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Hape në shfletues"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Dritare e re"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Menaxho dritaret"</string>
     <string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Hap menynë"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Përmasat e këtij aplikacioni nuk mund të ndryshohen"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 87ae78e..9545ccf 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Снимак екрана"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Отворите у прегледачу"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Нови прозор"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Управљајте прозорима"</string>
     <string name="close_text" msgid="4986518933445178928">"Затворите"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Отворите мени"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Величина ове апликације не може да се промени"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 6942e95..c43f5df 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Skärmbild"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Öppna i webbläsaren"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Nytt fönster"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Hantera fönster"</string>
     <string name="close_text" msgid="4986518933445178928">"Stäng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Öppna menyn"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Det går inte att ändra storlek på appen"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 30d6870..c6ce023 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Picha ya skrini"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Fungua katika kivinjari"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Dirisha Jipya"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Dhibiti Windows"</string>
     <string name="close_text" msgid="4986518933445178928">"Funga"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Fungua Menyu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Huwezi kubadilisha ukubwa wa programu hii"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 9e51416..2c2f319 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ஸ்கிரீன்ஷாட்"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"உலாவியில் திறக்கும்"</string>
     <string name="new_window_text" msgid="6318648868380652280">"புதிய சாளரம்"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"சாளரங்களை நிர்வகிக்கலாம்"</string>
     <string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"மெனுவைத் திற"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"இந்த ஆப்ஸின் அளவை மாற்ற முடியாது"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index be770ca..8691c9d 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"స్క్రీన్‌షాట్"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"బ్రౌజర్‌లో తెరవండి"</string>
     <string name="new_window_text" msgid="6318648868380652280">"కొత్త విండో"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"విండోలను మేనేజ్ చేయండి"</string>
     <string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"మెనూను తెరవండి"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్‌ను పెంచండి"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్‌ను స్నాప్ చేయండి"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ఈ యాప్ సైజ్‌ను మార్చడం సాధ్యపడదు"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index e7975ac..07e1416 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"ภาพหน้าจอ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"เปิดในเบราว์เซอร์"</string>
     <string name="new_window_text" msgid="6318648868380652280">"หน้าต่างใหม่"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"จัดการหน้าต่าง"</string>
     <string name="close_text" msgid="4986518933445178928">"ปิด"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"เปิดเมนู"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ปรับขนาดแอปนี้ไม่ได้"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 72d0926..477700e 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Buksan sa browser"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Bagong Window"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Pamahalaan ang Mga Window"</string>
     <string name="close_text" msgid="4986518933445178928">"Isara"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Buksan ang Menu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hindi nare-resize ang app na ito"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 2b02f47..b0c2539 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Ekran görüntüsü"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Tarayıcıda aç"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Yeni Pencere"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Pencereleri yönet"</string>
     <string name="close_text" msgid="4986518933445178928">"Kapat"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Menüyü Aç"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu uygulama yeniden boyutlandırılamaz"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 47126ac..dd64c66 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Знімок екрана"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Відкрити у вебпереглядачі"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Нове вікно"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Керувати вікнами"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрити"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Відкрити меню"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Розмір вікна цього додатка не можна змінити"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 859288f..aa311c2 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"اسکرین شاٹ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"براؤزر میں کھولیں"</string>
     <string name="new_window_text" msgid="6318648868380652280">"نئی ونڈو"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"‏‫Windows کا نظم کریں"</string>
     <string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"مینو کھولیں"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اس ایپ کا سائز تبدیل نہیں کیا جا سکتا"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 625fc8e..8754629 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Skrinshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerda ochish"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Yangi oyna"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Oynalarni boshqarish"</string>
     <string name="close_text" msgid="4986518933445178928">"Yopish"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Menyuni ochish"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu ilova hajmini oʻzgartirish imkonsiz"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 2e643dd..abac3fe 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Ảnh chụp màn hình"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Mở trong trình duyệt"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Cửa sổ mới"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Quản lý cửa sổ"</string>
     <string name="close_text" msgid="4986518933445178928">"Đóng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Mở Trình đơn"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Không thể đổi kích thước của ứng dụng này"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index f023f53..5a61dec 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"屏幕截图"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"在浏览器中打开"</string>
     <string name="new_window_text" msgid="6318648868380652280">"新窗口"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"管理窗口"</string>
     <string name="close_text" msgid="4986518933445178928">"关闭"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"打开菜单"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"无法调整此应用的大小"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 5c2ef04..e1c303e 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"螢幕截圖"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
     <string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"打開選單"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"此應用程式無法調整大小"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index a362d5b..97e36c3 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"螢幕截圖"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
     <string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"開啟選單"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"這個應用程式無法調整大小"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 3a3f431..74c7169 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -123,10 +123,18 @@
     <string name="screenshot_text" msgid="1477704010087786671">"Isithombe-skrini"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Vula kubhrawuza"</string>
     <string name="new_window_text" msgid="6318648868380652280">"Iwindi Elisha"</string>
+    <string name="manage_windows_text" msgid="5567366688493093920">"Phatha Amawindi"</string>
     <string name="close_text" msgid="4986518933445178928">"Vala"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
-    <string name="expand_menu_text" msgid="3847736164494181168">"Vula Imenyu"</string>
+    <!-- no translation found for desktop_mode_app_header_chip_text (6366422614991687237) -->
+    <skip />
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string>
     <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Le app ayikwazi ukushintshwa usayizi"</string>
+    <!-- no translation found for desktop_mode_maximize_menu_maximize_button_text (3090199175564175845) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_left_button_text (8077452201179893424) -->
+    <skip />
+    <!-- no translation found for desktop_mode_maximize_menu_snap_right_button_text (7117751068945657304) -->
+    <skip />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index fe8b818..a14461a 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -17,7 +17,7 @@
 <resources>
     <!-- Determines whether the shell features all run on another thread. This is to be overrided
          by the resources of the app using the Shell library. -->
-    <bool name="config_enableShellMainThread">false</bool>
+    <bool name="config_enableShellMainThread">true</bool>
 
     <!-- Determines whether to register the shell task organizer on init.
          TODO(b/238217847): This config is temporary until we refactor the base WMComponent. -->
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 3d87183..c7109f5 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -608,6 +608,9 @@
     <!-- The horizontal inset to apply to the close button's ripple drawable -->
     <dimen name="desktop_mode_header_close_ripple_inset_horizontal">6dp</dimen>
 
+    <!-- The padding added to all sides of windowing education tooltip -->
+    <dimen name="desktop_windowing_education_tooltip_padding">8dp</dimen>
+
     <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) -->
     <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item>
     <!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) -->
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index a6da421..56f25da 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -219,6 +219,15 @@
          compatibility control. [CHAR LIMIT=NONE] -->
     <string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
 
+    <!-- App handle education tooltip text for tooltip pointing to app handle -->
+    <string name="windowing_app_handle_education_tooltip">Tap to open the app menu</string>
+
+    <!-- App handle education tooltip text for tooltip pointing to windowing image button -->
+    <string name="windowing_desktop_mode_image_button_education_tooltip">Tap to show multiple apps together</string>
+
+    <!-- App handle education tooltip text for tooltip pointing to app chip -->
+    <string name="windowing_desktop_mode_exit_education_tooltip">Return to fullscreen from the app menu</string>
+
     <!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] -->
     <string name="letterbox_education_dialog_title">See and do more</string>
 
@@ -300,12 +309,18 @@
     <string name="close_text">Close</string>
     <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
     <string name="collapse_menu_text">Close Menu</string>
-    <!-- Accessibility text for the handle menu open menu button [CHAR LIMIT=NONE] -->
-    <string name="expand_menu_text">Open Menu</string>
+    <!-- Accessibility text for the App Header's App Chip [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_app_header_chip_text">Open Menu</string>
     <!-- Maximize menu maximize button string. -->
     <string name="desktop_mode_maximize_menu_maximize_text">Maximize Screen</string>
     <!-- Maximize menu snap buttons string. -->
     <string name="desktop_mode_maximize_menu_snap_text">Snap Screen</string>
     <!-- Snap resizing non-resizable string. -->
-    <string name="desktop_mode_non_resizable_snap_text">This app can\'t be resized</string>
+    <string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
+    <!-- Accessibility text for the Maximize Menu's maximize button [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_maximize_menu_maximize_button_text">Maximize</string>
+    <!-- Accessibility text for the Maximize Menu's snap left button [CHAR LIMIT=NONE] -->
+    <string name="desktop_mode_maximize_menu_snap_left_button_text">Snap left</string>
+    <!-- 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>
 </resources>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java
new file mode 100644
index 0000000..26aae2d
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/FocusTransitionListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+import com.android.wm.shell.shared.annotations.ExternalThread;
+
+/**
+ * Listener to get focus-related transition callbacks.
+ */
+@ExternalThread
+public interface FocusTransitionListener {
+    /**
+     * Called when a transition changes the top, focused display.
+     */
+    void onFocusedDisplayChanged(int displayId);
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IFocusTransitionListener.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IFocusTransitionListener.aidl
new file mode 100644
index 0000000..b91d5b6
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IFocusTransitionListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Listener interface that to get focus-related transition callbacks.
+ */
+oneway interface IFocusTransitionListener {
+
+    /**
+     * Called when a transition changes the top, focused display.
+     */
+    void onFocusedDisplayChanged(int displayId);
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl
index 3256abf..02615a9 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IShellTransitions.aidl
@@ -20,6 +20,7 @@
 import android.window.RemoteTransition;
 import android.window.TransitionFilter;
 
+import com.android.wm.shell.shared.IFocusTransitionListener;
 import com.android.wm.shell.shared.IHomeTransitionListener;
 
 /**
@@ -59,4 +60,9 @@
      */
     oneway void registerRemoteForTakeover(in TransitionFilter filter,
             in RemoteTransition remoteTransition) = 6;
+
+    /**
+     * Set listener that will receive callbacks about transitions involving focus switch.
+     */
+    oneway void setFocusTransitionListener(in IFocusTransitionListener listener) = 7;
 }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java
index 6d4ab4c..2db4311 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellTransitions.java
@@ -22,6 +22,8 @@
 
 import com.android.wm.shell.shared.annotations.ExternalThread;
 
+import java.util.concurrent.Executor;
+
 /**
  * Interface to manage remote transitions.
  */
@@ -44,4 +46,15 @@
      * Unregisters a remote transition for all operations.
      */
     default void unregisterRemote(@NonNull RemoteTransition remoteTransition) {}
+
+    /**
+     * Sets listener that will receive callbacks about transitions involving focus switch.
+     */
+    default void setFocusTransitionListener(@NonNull FocusTransitionListener listener,
+            Executor executor) {}
+
+    /**
+     * Unsets listener that will receive callbacks about transitions involving focus switch.
+     */
+    default void unsetFocusTransitionListener(@NonNull FocusTransitionListener listener) {}
 }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
deleted file mode 100644
index b5d63bd..0000000
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ /dev/null
@@ -1,143 +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.shared.desktopmode
-
-import android.content.Context
-import android.provider.Settings
-import android.util.Log
-import com.android.window.flags.Flags
-
-/*
- * An enum to check desktop mode flags state.
- *
- * This enum provides a centralized way to control the behavior of flags related to desktop
- * windowing features which are aiming for developer preview before their release. It allows
- * developer option to override the default behavior of these flags.
- *
- * 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.
- */
-enum class DesktopModeFlags(
-    // Function called to obtain aconfig flag value.
-    private val flagFunction: () -> Boolean,
-    // Whether the flag state should be affected by developer option.
-    private val shouldOverrideByDevOption: Boolean
-) {
-    // All desktop mode related flags will be added here
-    DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true),
-    CASCADING_WINDOWS(Flags::enableCascadingWindows, true),
-    WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true),
-    MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true),
-    THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
-    QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
-    APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
-    TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
-    SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
-    DISABLE_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
-    DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false),
-    SCALED_RESIZING(Flags::enableWindowingScaledResizing, false),
-    ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
-    BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
-    EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
-    TASKBAR_RUNNING_APPS(Flags::enableDesktopWindowingTaskbarRunningApps, true);
-
-    /**
-     * Determines state of flag based on the actual flag and desktop mode developer option
-     * overrides.
-     */
-    fun isEnabled(context: Context): Boolean =
-        if (!Flags.showDesktopWindowingDevOption() ||
-            !shouldOverrideByDevOption ||
-            context.contentResolver == null) {
-            flagFunction()
-        } else {
-            val shouldToggleBeEnabledByDefault =
-                DesktopModeStatus.shouldDevOptionBeEnabledByDefault()
-            when (getToggleOverride(context)) {
-                ToggleOverride.OVERRIDE_UNSET -> flagFunction()
-                // When toggle override matches its default state, don't override flags. This helps
-                // users reset their feature overrides.
-                ToggleOverride.OVERRIDE_OFF ->
-                    if (shouldToggleBeEnabledByDefault) false else flagFunction()
-                ToggleOverride.OVERRIDE_ON ->
-                    if (shouldToggleBeEnabledByDefault) flagFunction() else true
-            }
-        }
-
-    private fun getToggleOverride(context: Context): ToggleOverride {
-        val override =
-            cachedToggleOverride
-                ?: run {
-                    val override = getToggleOverrideFromSystem(context)
-                    // Cache toggle override the first time we encounter context. Override does not
-                    // change with context, as context is just used to fetch Settings.Global
-                    cachedToggleOverride = override
-                    Log.d(TAG, "Toggle override initialized to: $override")
-                    override
-                }
-
-        return override
-    }
-
-    private fun getToggleOverrideFromSystem(context: Context): ToggleOverride =
-        convertToToggleOverrideWithFallback(
-            Settings.Global.getInt(
-                context.contentResolver,
-                Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
-                ToggleOverride.OVERRIDE_UNSET.setting),
-            ToggleOverride.OVERRIDE_UNSET)
-
-    /**
-     * Override state of desktop mode developer option toggle.
-     *
-     * @property setting The integer value that is associated with the developer option toggle
-     *   override
-     */
-    enum class ToggleOverride(val setting: Int) {
-        /** No override is set. */
-        OVERRIDE_UNSET(-1),
-        /** Override to off. */
-        OVERRIDE_OFF(0),
-        /** Override to on. */
-        OVERRIDE_ON(1)
-    }
-
-    companion object {
-        private const val TAG = "DesktopModeFlags"
-
-        /**
-         * 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.
-         */
-        private var cachedToggleOverride: ToggleOverride? = null
-
-        private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting }
-
-        @JvmStatic
-        fun convertToToggleOverrideWithFallback(
-            overrideInt: Int,
-            fallbackOverride: ToggleOverride
-        ): ToggleOverride {
-            return settingToToggleOverrideMap[overrideInt]
-                ?: run {
-                    Log.w(TAG, "Unknown toggleOverride int $overrideInt")
-                    fallbackOverride
-                }
-        }
-    }
-}
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 83619ef..647a555a 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
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.SystemProperties;
+import android.window.flags.DesktopModeFlags;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -177,7 +178,7 @@
     public static boolean canEnterDesktopMode(@NonNull Context context) {
         if (!isDeviceEligibleForDesktopMode(context)) return false;
 
-        return DesktopModeFlags.DESKTOP_WINDOWING_MODE.isEnabled(context);
+        return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue();
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 7e6f434..4607a8e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -584,7 +584,8 @@
             final boolean windowModeChanged =
                     data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode();
             final boolean visibilityChanged = data.getTaskInfo().isVisible != taskInfo.isVisible;
-            if (windowModeChanged || visibilityChanged) {
+            if (windowModeChanged || (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+                    && visibilityChanged)) {
                 mRecentTasks.ifPresent(recentTasks ->
                         recentTasks.onTaskRunningInfoChanged(taskInfo));
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 17869e9..4d15605c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -27,6 +27,7 @@
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.window.flags.DesktopModeFlags;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.window.flags.Flags;
@@ -37,7 +38,6 @@
 import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
 import com.android.wm.shell.compatui.api.CompatUIEvent;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
 import java.util.function.Consumer;
@@ -83,7 +83,7 @@
         mCallback = callback;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat();
         if (DesktopModeStatus.canEnterDesktopMode(context)
-                && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
+                && DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
         }
@@ -139,7 +139,7 @@
         final boolean prevHasSizeCompat = mHasSizeCompat;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat();
         if (DesktopModeStatus.canEnterDesktopMode(mContext)
-                && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)) {
+                && DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
         }
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 722fe1f..4227a6e 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
@@ -123,6 +123,7 @@
 import com.android.wm.shell.taskview.TaskViewFactory;
 import com.android.wm.shell.taskview.TaskViewFactoryController;
 import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.HomeTransitionObserver;
 import com.android.wm.shell.transition.MixedTransitionHandler;
 import com.android.wm.shell.transition.Transitions;
@@ -742,14 +743,15 @@
             @ShellMainThread Handler mainHandler,
             @ShellAnimationThread ShellExecutor animExecutor,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            HomeTransitionObserver homeTransitionObserver) {
+            HomeTransitionObserver homeTransitionObserver,
+            FocusTransitionObserver focusTransitionObserver) {
         if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) {
             // TODO(b/238217847): Force override shell init if registration is disabled
             shellInit = new ShellInit(mainExecutor);
         }
         return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer,
                 pool, displayController, mainExecutor, mainHandler, animExecutor,
-                rootTaskDisplayAreaOrganizer, homeTransitionObserver);
+                rootTaskDisplayAreaOrganizer, homeTransitionObserver, focusTransitionObserver);
     }
 
     @WMSingleton
@@ -761,6 +763,12 @@
 
     @WMSingleton
     @Provides
+    static FocusTransitionObserver provideFocusTransitionObserver() {
+        return new FocusTransitionObserver();
+    }
+
+    @WMSingleton
+    @Provides
     static TaskViewTransitions provideTaskViewTransitions(Transitions transitions) {
         return new TaskViewTransitions(transitions);
     }
@@ -1019,11 +1027,10 @@
     @WMSingleton
     @Provides
     static TaskStackTransitionObserver provideTaskStackTransitionObserver(
-            Context context,
             Lazy<Transitions> transitions,
             ShellInit shellInit
     ) {
-        return new TaskStackTransitionObserver(context, transitions, shellInit);
+        return new TaskStackTransitionObserver(transitions, shellInit);
     }
 
     //
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 584f272..96a0775 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,9 +16,8 @@
 
 package com.android.wm.shell.dagger;
 
-import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
+import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.content.Context;
@@ -79,6 +78,7 @@
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
 import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter;
 import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
+import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.draganddrop.GlobalDragListener;
 import com.android.wm.shell.freeform.FreeformComponents;
@@ -116,8 +116,6 @@
 import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-import com.android.wm.shell.windowdecor.viewhost.DefaultWindowDecorViewHostSupplier;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import dagger.Binds;
 import dagger.Lazy;
@@ -249,8 +247,7 @@
             MultiInstanceHelper multiInstanceHelper,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return new DesktopModeWindowDecorViewModel(
                     context,
@@ -275,8 +272,7 @@
                     multiInstanceHelper,
                     desktopTasksLimiter,
                     windowDecorCaptionHandleRepository,
-                    desktopActivityOrientationHandler,
-                    windowDecorViewHostSupplier);
+                    desktopActivityOrientationHandler);
         }
         return new CaptionWindowDecorViewModel(
                 context,
@@ -290,8 +286,7 @@
                 displayController,
                 rootTaskDisplayAreaOrganizer,
                 syncQueue,
-                transitions,
-                windowDecorViewHostSupplier);
+                transitions);
     }
 
     @WMSingleton
@@ -382,13 +377,6 @@
                 context, shellInit, transitions, windowDecorViewModel);
     }
 
-    @WMSingleton
-    @Provides
-    static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier(
-            @ShellMainThread @NonNull CoroutineScope mainScope) {
-        return new DefaultWindowDecorViewHostSupplier(mainScope);
-    }
-
     //
     // One handed mode
     //
@@ -636,7 +624,7 @@
             @ShellMainThread Handler handler) {
         int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context);
         if (!DesktopModeStatus.canEnterDesktopMode(context)
-                || !ENABLE_DESKTOP_WINDOWING_TASK_LIMIT.isEnabled(context)
+                || !ENABLE_DESKTOP_WINDOWING_TASK_LIMIT.isTrue()
                 || maxTaskLimit <= 0) {
             return Optional.empty();
         }
@@ -712,8 +700,14 @@
     @WMSingleton
     @Provides
     @DynamicOverride
-    static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
-        return new DesktopModeTaskRepository();
+    static DesktopModeTaskRepository provideDesktopModeTaskRepository(
+            Context context,
+            ShellInit shellInit,
+            DesktopPersistentRepository desktopPersistentRepository,
+            @ShellMainThread CoroutineScope mainScope
+    ) {
+        return new DesktopModeTaskRepository(context, shellInit, desktopPersistentRepository,
+                mainScope);
     }
 
     @WMSingleton
@@ -798,6 +792,14 @@
                 shellTaskOrganizer, appHandleEducationDatastoreRepository, applicationScope);
     }
 
+    @WMSingleton
+    @Provides
+    static DesktopPersistentRepository provideDesktopPersistentRepository(
+            Context context,
+            @ShellBackgroundThread CoroutineScope bgScope) {
+        return new DesktopPersistentRepository(context, bgScope);
+    }
+
     //
     // Drag and drop
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 9d04169..0e8c4e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.content.Context
 import android.graphics.Rect
 import android.graphics.Region
 import android.util.ArrayMap
@@ -27,13 +28,27 @@
 import androidx.core.util.keyIterator
 import androidx.core.util.valueIterator
 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.DesktopTask
+import com.android.wm.shell.desktopmode.persistence.DesktopTaskState
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+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.io.PrintWriter
 import java.util.concurrent.Executor
 import java.util.function.Consumer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 /** Tracks task data for Desktop Mode. */
-class DesktopModeTaskRepository {
+class DesktopModeTaskRepository (
+    private val context: Context,
+    shellInit: ShellInit,
+    private val persistentRepository: DesktopPersistentRepository,
+    @ShellMainThread private val mainCoroutineScope: CoroutineScope,
+){
 
     /**
      * Task data tracked per desktop.
@@ -54,7 +69,15 @@
         // TODO(b/332682201): Remove when the repository state is updated via TransitionObserver
         val closingTasks: ArraySet<Int> = ArraySet(),
         val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
-    )
+    ) {
+        fun deepCopy(): DesktopTaskData = DesktopTaskData(
+            activeTasks = ArraySet(activeTasks),
+            visibleTasks = ArraySet(visibleTasks),
+            minimizedTasks = ArraySet(minimizedTasks),
+            closingTasks = ArraySet(closingTasks),
+            freeformTasksInZOrder = ArrayList(freeformTasksInZOrder)
+        )
+    }
 
     /* Current wallpaper activity token to remove wallpaper activity when last task is removed. */
     var wallpaperActivityToken: WindowContainerToken? = null
@@ -77,6 +100,40 @@
             this[displayId] ?: DesktopTaskData().also { this[displayId] = it }
     }
 
+    init {
+        if (DesktopModeStatus.canEnterDesktopMode(context)) {
+            shellInit.addInitCallback(::initRepoFromPersistentStorage, this)
+        }
+    }
+
+    private fun initRepoFromPersistentStorage() {
+        if (!Flags.enableDesktopWindowingPersistence()) return
+        //  TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized
+        mainCoroutineScope.launch {
+            val desktop = persistentRepository.readDesktop()
+            val maxTasks =
+                DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
+                    ?: desktop.zOrderedTasksCount
+
+            desktop.zOrderedTasksList
+                // Reverse it so we initialize the repo from bottom to top.
+                .reversed()
+                .map { taskId ->
+                    desktop.tasksByTaskIdMap.getOrDefault(
+                        taskId,
+                        DesktopTask.getDefaultInstance()
+                    )
+                }
+                .filter { task -> task.desktopTaskState == DesktopTaskState.VISIBLE }
+                .take(maxTasks)
+                .forEach { task ->
+                    addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId)
+                    addActiveTask(desktop.displayId, task.taskId)
+                    updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
+                }
+        }
+    }
+
     /** Adds [activeTasksListener] to be notified of updates to active tasks. */
     fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) {
         activeTasksListeners.add(activeTasksListener)
@@ -266,12 +323,18 @@
         desktopTaskDataByDisplayId.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
         // Unminimize the task if it is minimized.
         unminimizeTask(displayId, taskId)
+        if (Flags.enableDesktopWindowingPersistence()) {
+            updatePersistentRepository(displayId)
+        }
     }
 
     /** Minimizes the task for [taskId] and [displayId] */
     fun minimizeTask(displayId: Int, taskId: Int) {
         logD("Minimize Task: display=%d, task=%d", displayId, taskId)
         desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
+        if (Flags.enableDesktopWindowingPersistence()) {
+            updatePersistentRepository(displayId)
+        }
     }
 
     /** Unminimizes the task for [taskId] and [displayId] */
@@ -315,7 +378,10 @@
         // Remove task from unminimized task if it is minimized.
         unminimizeTask(displayId, taskId)
         removeActiveTask(taskId)
-        updateTaskVisibility(displayId, taskId, visible = false);
+        updateTaskVisibility(displayId, taskId, visible = false)
+        if (Flags.enableDesktopWindowingPersistence()) {
+            updatePersistentRepository(displayId)
+        }
     }
 
     /**
@@ -352,6 +418,27 @@
     fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) =
         boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
 
+    private fun updatePersistentRepository(displayId: Int) {
+        // Create a deep copy of the data
+        desktopTaskDataByDisplayId[displayId]?.deepCopy()?.let { desktopTaskDataByDisplayIdCopy ->
+            mainCoroutineScope.launch {
+                try {
+                    persistentRepository.addOrUpdateDesktop(
+                        visibleTasks = desktopTaskDataByDisplayIdCopy.visibleTasks,
+                        minimizedTasks = desktopTaskDataByDisplayIdCopy.minimizedTasks,
+                        freeformTasksInZOrder = desktopTaskDataByDisplayIdCopy.freeformTasksInZOrder
+                    )
+                } catch (exception: Exception) {
+                    logE(
+                        "An exception occurred while updating the persistent repository \n%s",
+                        exception.stackTrace
+                    )
+                }
+            }
+        }
+    }
+
+
     internal fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopModeTaskRepository")
@@ -369,6 +456,7 @@
             pw.println(
                 "${innerPrefix}freeformTasksInZOrder=${data.freeformTasksInZOrder.toDumpString()}"
             )
+            pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}")
         }
     }
 
@@ -390,6 +478,10 @@
         ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
     }
 
+    private fun logE(msg: String, vararg arguments: Any?) {
+        ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+    }
+
     companion object {
         private const val TAG = "DesktopModeTaskRepository"
     }
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 b8ebbcd..afa27f9 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
@@ -43,6 +43,7 @@
 import android.view.DragEvent
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_CLOSE
 import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
@@ -58,6 +59,7 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.policy.ScreenDecorationsUtils
 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
@@ -80,12 +82,14 @@
 import com.android.wm.shell.recents.RecentTasksController
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
-import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.shared.ShellSharedConstants
+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.DesktopModeFlags
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY
+import android.window.flags.DesktopModeFlags
+import android.window.flags.DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE
+import android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+import android.window.flags.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
 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
@@ -364,7 +368,7 @@
         wct: WindowContainerTransaction = WindowContainerTransaction(),
         transitionSource: DesktopModeTransitionSource,
     ) {
-        if (DesktopModeFlags.MODALS_POLICY.isEnabled(context)
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
             && isTopActivityExemptFromDesktopWindowing(context, task)) {
             logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId)
             return
@@ -642,7 +646,7 @@
             if (taskBoundsBeforeMaximize != null) {
                 destinationBounds.set(taskBoundsBeforeMaximize)
             } else {
-                if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
+                if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
                     destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo))
                 } else {
                     destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
@@ -726,7 +730,7 @@
                 // exclude current task since maximize/restore transition has not taken place yet.
                 .filterNot { taskId -> taskId == excludeTaskId }
                 .any { taskId ->
-                    val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)!!
+                    val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return false
                     val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
                     val stableBounds = Rect().apply { displayLayout?.getStableBounds(this) }
                     logD("taskInfo = %s", taskInfo)
@@ -796,7 +800,7 @@
         dragStartBounds: Rect
     ) {
         releaseVisualIndicator()
-        if (!taskInfo.isResizeable && DesktopModeFlags.DISABLE_SNAP_RESIZE.isEnabled(context)) {
+        if (!taskInfo.isResizeable && DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()) {
             interactionJankMonitor.begin(
                 taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_non_resizable"
             )
@@ -885,7 +889,8 @@
         moveHomeTask(wct, toTop = true)
 
         // Currently, we only handle the desktop on the default display really.
-        if (displayId == DEFAULT_DISPLAY && WALLPAPER_ACTIVITY.isEnabled(context)) {
+        if (displayId == DEFAULT_DISPLAY
+            && ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
             // Add translucent wallpaper activity to show the wallpaper underneath
             addWallpaperActivity(wct)
         }
@@ -893,6 +898,7 @@
         val nonMinimizedTasksOrderedFrontToBack =
             taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
         // If we're adding a new Task we might need to minimize an old one
+        // TODO(b/365725441): Handle non running task minimization
         val taskToMinimize: RunningTaskInfo? =
             if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
                 desktopTasksLimiter
@@ -904,12 +910,26 @@
             } else {
                 null
             }
+
         nonMinimizedTasksOrderedFrontToBack
             // If there is a Task to minimize, let it stay behind the Home Task
             .filter { taskId -> taskId != taskToMinimize?.taskId }
-            .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
             .reversed() // Start from the back so the front task is brought forward last
-            .forEach { task -> wct.reorder(task.token, /* onTop= */ true) }
+            .forEach { taskId ->
+                val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)
+                if (runningTaskInfo != null) {
+                    // Task is already running, reorder it to the front
+                    wct.reorder(runningTaskInfo.token, /* onTop= */ true)
+                } else if (Flags.enableDesktopWindowingPersistence()) {
+                    // Task is not running, start it
+                    wct.startTask(
+                        taskId,
+                        ActivityOptions.makeBasic().apply {
+                            launchWindowingMode = WINDOWING_MODE_FREEFORM
+                        }.toBundle(),
+                    )
+                }
+            }
 
         taskbarDesktopTaskListener?.
             onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
@@ -1042,7 +1062,10 @@
                     // Check if freeform task launch during recents should be handled
                     shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task)
                     // Check if the closing task needs to be handled
-                    TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task)
+                    TransitionUtil.isClosingType(request.type) -> handleTaskClosing(
+                        task,
+                        request.type
+                    )
                     // Check if the top task shouldn't be allowed to enter desktop mode
                     isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)
                     // Check if fullscreen task should be updated
@@ -1084,11 +1107,11 @@
                 && taskRepository.isActiveTask(triggerTask.taskId))
 
     private fun isIncompatibleTask(task: TaskInfo) =
-        DesktopModeFlags.MODALS_POLICY.isEnabled(context)
+        DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
                 && isTopActivityExemptFromDesktopWindowing(context, task)
 
     private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean {
-        return WALLPAPER_ACTIVITY.isEnabled(context) &&
+        return ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() &&
             TransitionUtil.isClosingType(request.type) &&
             request.triggerTask != null
     }
@@ -1208,9 +1231,10 @@
             wct.reorder(task.token, true)
             return wct
         }
+        // TODO(b/365723620): Handle non running tasks that were launched after reboot.
         // If task is already visible, it must have been handled already and added to desktop mode.
         // Cascade task only if it's not visible yet.
-        if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)
+        if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue()
                 && !taskRepository.isVisibleTask(task.taskId)) {
             val displayLayout = displayController.getDisplayLayout(task.displayId)
             if (displayLayout != null) {
@@ -1268,7 +1292,10 @@
     }
 
     /** Handle task closing by removing wallpaper activity if it's the last active task */
-    private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
+    private fun handleTaskClosing(
+        task: RunningTaskInfo,
+        transitionType: Int
+    ): WindowContainerTransaction? {
         logV("handleTaskClosing")
         if (!isDesktopModeShowing(task.displayId))
             return null
@@ -1281,9 +1308,10 @@
             removeWallpaperActivity(wct)
         }
         taskRepository.addClosingTask(task.displayId, task.taskId)
-        // If a CLOSE or TO_BACK is triggered on a desktop task, remove the task.
-        if (DesktopModeFlags.BACK_NAVIGATION.isEnabled(context) &&
-            taskRepository.isVisibleTask(task.taskId)
+        // If a CLOSE is triggered on a desktop task, remove the task.
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() &&
+            taskRepository.isVisibleTask(task.taskId) &&
+            transitionType == TRANSIT_CLOSE
         ) {
             wct.removeTask(task.token)
         }
@@ -1311,13 +1339,13 @@
             } else {
                 WINDOWING_MODE_FREEFORM
             }
-        val initialBounds = if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
+        val initialBounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
             calculateInitialBounds(displayLayout, taskInfo)
         } else {
             getDefaultDesktopTaskBounds(displayLayout)
         }
 
-        if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)) {
+        if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue()) {
             cascadeWindow(taskInfo, initialBounds, displayLayout)
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index dae37f4..d84349b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -38,7 +38,7 @@
  * Limits the number of tasks shown in Desktop Mode.
  *
  * This class should only be used if
- * [com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT]
+ * [android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT]
  * is enabled and [maxTasksLimit] is strictly greater than 0.
  */
 class DesktopTasksLimiter (
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 74e53fa..4796c4d 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
@@ -16,16 +16,19 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.content.Context
 import android.os.IBinder
 import android.view.SurfaceControl
 import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_TO_BACK
 import android.window.TransitionInfo
 import android.window.WindowContainerTransaction
+import android.window.flags.DesktopModeFlags
+import android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
@@ -64,6 +67,30 @@
     ) {
         // TODO: b/332682201 Update repository state
         updateWallpaperToken(info)
+
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
+            handleBackNavigation(info)
+        }
+    }
+
+    private fun handleBackNavigation(info: TransitionInfo) {
+        // When default back navigation happens, transition type is TO_BACK and the change is
+        // TO_BACK. Mark the task going to back as minimized.
+        if (info.type == TRANSIT_TO_BACK) {
+            for (change in info.changes) {
+                val taskInfo = change.taskInfo
+                if (taskInfo == null || taskInfo.taskId == -1) {
+                    continue
+                }
+
+                if (desktopModeTaskRepository.getVisibleTaskCount(taskInfo.displayId) > 0 &&
+                    change.mode == TRANSIT_TO_BACK &&
+                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+                ) {
+                    desktopModeTaskRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+                }
+            }
+        }
     }
 
     override fun onTransitionStarting(transition: IBinder) {
@@ -79,7 +106,7 @@
     }
 
     private fun updateWallpaperToken(info: TransitionInfo) {
-        if (!WALLPAPER_ACTIVITY.isEnabled(context)) {
+        if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
             return
         }
         info.changes.forEach { change ->
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 dfc5ab3..2bc01b2 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
@@ -304,14 +304,14 @@
         val leafTaskFilter = TransitionUtil.LeafTaskFilter()
         info.changes.withIndex().forEach { (i, change) ->
             if (TransitionUtil.isWallpaper(change)) {
-                val layer = layers.wallpaperLayers - i
+                val layer = layers.topWallpaperLayer - i
                 startTransaction.apply {
                     setLayer(change.leash, layer)
                     show(change.leash)
                 }
             } else if (isHomeChange(change)) {
                 state.homeChange = change
-                val layer = layers.homeLayers - i
+                val layer = layers.topHomeLayer - i
                 startTransaction.apply {
                     setLayer(change.leash, layer)
                     show(change.leash)
@@ -325,7 +325,7 @@
                             if (state.cancelState == CancelState.NO_CANCEL) {
                                 // Normal case, split root goes to the bottom behind everything
                                 // else.
-                                layers.appLayers - i
+                                layers.topAppLayer - i
                             } else {
                                 // Cancel-early case, pretend nothing happened so split root stays
                                 // top.
@@ -357,7 +357,7 @@
                             state.otherRootChanges.add(change)
                             val bounds = change.endAbsBounds
                             startTransaction.apply {
-                                setLayer(change.leash, layers.appLayers - i)
+                                setLayer(change.leash, layers.topAppLayer - i)
                                 setWindowCrop(change.leash, bounds.width(), bounds.height())
                                 show(change.leash)
                             }
@@ -398,6 +398,7 @@
                 }
             }
         }
+        state.surfaceLayers = layers
         state.startTransitionFinishCb = finishCallback
         state.startTransitionFinishTransaction = finishTransaction
         startTransaction.apply()
@@ -522,6 +523,10 @@
                     startTransaction.show(change.leash)
                     finishTransaction.show(change.leash)
                     state.draggedTaskChange = change
+                    // Restoring the dragged leash layer as it gets reset in the merge transition
+                    state.surfaceLayers?.let {
+                        startTransaction.setLayer(change.leash, it.dragLayer)
+                    }
                 }
                 change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM -> {
                     // Other freeform tasks that are being restored go behind the dragged task.
@@ -647,8 +652,15 @@
         }
     }
 
-    private fun isHomeChange(change: Change): Boolean {
-        return change.taskInfo?.activityType == ACTIVITY_TYPE_HOME
+    /** Checks if the change is a home task change */
+    @VisibleForTesting
+    fun isHomeChange(change: Change): Boolean {
+        return change.taskInfo?.let {
+            it.activityType == ACTIVITY_TYPE_HOME &&
+                // Skip translucent wizard task with type home
+                // TODO(b/368334295): Remove when the multiple home changes issue is resolved
+                !(it.isTopActivityTransparent && it.numActivities == 1)
+        } ?: false
     }
 
     private fun startCancelAnimation() {
@@ -765,12 +777,18 @@
 
     /**
      * Represents the layering (Z order) that will be given to any window based on its type during
-     * the "start" transition of the drag-to-desktop transition
+     * the "start" transition of the drag-to-desktop transition.
+     *
+     * @param topAppLayer Used to calculate the app layer z-order = `topAppLayer - changeIndex`.
+     * @param topHomeLayer Used to calculate the home layer z-order = `topHomeLayer - changeIndex`.
+     * @param topWallpaperLayer Used to calculate the wallpaper layer z-order = `topWallpaperLayer -
+     *   changeIndex`
+     * @param dragLayer Defines the drag layer z-order
      */
-    protected data class DragToDesktopLayers(
-        val appLayers: Int,
-        val homeLayers: Int,
-        val wallpaperLayers: Int,
+    data class DragToDesktopLayers(
+        val topAppLayer: Int,
+        val topHomeLayer: Int,
+        val topWallpaperLayer: Int,
         val dragLayer: Int,
     )
 
@@ -790,6 +808,7 @@
         abstract var homeChange: Change?
         abstract var draggedTaskChange: Change?
         abstract var freeformTaskChanges: List<Change>
+        abstract var surfaceLayers: DragToDesktopLayers?
         abstract var cancelState: CancelState
         abstract var startAborted: Boolean
 
@@ -803,6 +822,7 @@
             override var homeChange: Change? = null,
             override var draggedTaskChange: Change? = null,
             override var freeformTaskChanges: List<Change> = emptyList(),
+            override var surfaceLayers: DragToDesktopLayers? = null,
             override var cancelState: CancelState = CancelState.NO_CANCEL,
             override var startAborted: Boolean = false,
             var otherRootChanges: MutableList<Change> = mutableListOf()
@@ -818,6 +838,7 @@
             override var homeChange: Change? = null,
             override var draggedTaskChange: Change? = null,
             override var freeformTaskChanges: List<Change> = emptyList(),
+            override var surfaceLayers: DragToDesktopLayers? = null,
             override var cancelState: CancelState = CancelState.NO_CANCEL,
             override var startAborted: Boolean = false,
             var splitRootChange: Change? = null,
@@ -872,9 +893,9 @@
      */
     override fun calculateStartDragToDesktopLayers(info: TransitionInfo): DragToDesktopLayers =
         DragToDesktopLayers(
-            appLayers = info.changes.size,
-            homeLayers = info.changes.size * 2,
-            wallpaperLayers = info.changes.size * 3,
+            topAppLayer = info.changes.size,
+            topHomeLayer = info.changes.size * 2,
+            topWallpaperLayer = info.changes.size * 3,
             dragLayer = info.changes.size * 3
         )
 }
@@ -914,9 +935,9 @@
      */
     override fun calculateStartDragToDesktopLayers(info: TransitionInfo): DragToDesktopLayers =
         DragToDesktopLayers(
-            appLayers = -1,
-            homeLayers = info.changes.size - 1,
-            wallpaperLayers = info.changes.size * 2 - 1,
+            topAppLayer = -1,
+            topHomeLayer = info.changes.size - 1,
+            topWallpaperLayer = info.changes.size * 2 - 1,
             dragLayer = info.changes.size * 2
         )
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
new file mode 100644
index 0000000..3f41d7c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.persistence
+
+import android.content.Context
+import android.util.ArraySet
+import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.core.Serializer
+import androidx.datastore.dataStoreFile
+import com.android.framework.protobuf.InvalidProtocolBufferException
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.first
+
+/**
+ * Persistent repository for storing desktop mode related data.
+ *
+ * The main constructor is public only for testing purposes.
+ */
+class DesktopPersistentRepository(
+    private val dataStore: DataStore<DesktopPersistentRepositories>,
+) {
+    constructor(
+        context: Context,
+        @ShellBackgroundThread bgCoroutineScope: CoroutineScope,
+    ) : this(
+        DataStoreFactory.create(
+            serializer = DesktopPersistentRepositoriesSerializer,
+            produceFile = { context.dataStoreFile(DESKTOP_REPOSITORIES_DATASTORE_FILE) },
+            scope = bgCoroutineScope))
+
+    /** Provides `dataStore.data` flow and handles exceptions thrown during collection */
+    private val dataStoreFlow: Flow<DesktopPersistentRepositories> =
+        dataStore.data.catch { exception ->
+            // dataStore.data throws an IOException when an error is encountered when reading data
+            if (exception is IOException) {
+                Log.e(
+                    TAG,
+                    "Error in reading desktop mode related data from datastore, data is " +
+                        "stored in a file named $DESKTOP_REPOSITORIES_DATASTORE_FILE",
+                    exception)
+            } else {
+                throw exception
+            }
+        }
+
+    /**
+     * Reads and returns the [DesktopRepositoryState] proto object from the DataStore for a user. If
+     * the DataStore is empty or there's an error reading, it returns the default value of Proto.
+     */
+    private suspend fun getDesktopRepositoryState(
+        userId: Int = DEFAULT_USER_ID
+    ): DesktopRepositoryState =
+        try {
+            dataStoreFlow
+                .first()
+                .desktopRepoByUserMap
+                .getOrDefault(userId, DesktopRepositoryState.getDefaultInstance())
+        } catch (e: Exception) {
+            Log.e(TAG, "Unable to read from datastore", e)
+            DesktopRepositoryState.getDefaultInstance()
+        }
+
+    /**
+     * Reads the [Desktop] of a desktop filtering by the [userId] and [desktopId]. Executes the
+     * [callback] using the [mainCoroutineScope].
+     */
+    suspend fun readDesktop(
+        userId: Int = DEFAULT_USER_ID,
+        desktopId: Int = DEFAULT_DESKTOP_ID,
+    ): Desktop =
+        try {
+            val repository = getDesktopRepositoryState(userId)
+            repository.getDesktopOrThrow(desktopId)
+        } catch (e: Exception) {
+            Log.e(TAG, "Unable to get desktop info from persistent repository", e)
+            Desktop.getDefaultInstance()
+        }
+
+    /** Adds or updates a desktop stored in the datastore */
+    suspend fun addOrUpdateDesktop(
+        userId: Int = DEFAULT_USER_ID,
+        desktopId: Int = 0,
+        visibleTasks: ArraySet<Int> = ArraySet(),
+        minimizedTasks: ArraySet<Int> = ArraySet(),
+        freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
+    ) {
+        // TODO: b/367609270 - Improve the API to support multi-user
+        try {
+            dataStore.updateData { desktopPersistentRepositories: DesktopPersistentRepositories ->
+                val currentRepository =
+                    desktopPersistentRepositories.getDesktopRepoByUserOrDefault(
+                        userId, DesktopRepositoryState.getDefaultInstance())
+                val desktop =
+                    getDesktop(currentRepository, desktopId)
+                        .toBuilder()
+                        .updateTaskStates(visibleTasks, minimizedTasks)
+                        .updateZOrder(freeformTasksInZOrder)
+
+                desktopPersistentRepositories
+                    .toBuilder()
+                    .putDesktopRepoByUser(
+                        userId,
+                        currentRepository
+                            .toBuilder()
+                            .putDesktop(desktopId, desktop.build())
+                            .build())
+                    .build()
+            }
+        } catch (exception: IOException) {
+            Log.e(
+                TAG,
+                "Error in updating desktop mode related data, data is " +
+                    "stored in a file named $DESKTOP_REPOSITORIES_DATASTORE_FILE",
+                exception)
+        }
+    }
+
+    private fun getDesktop(currentRepository: DesktopRepositoryState, desktopId: Int): Desktop =
+        // If there are no desktops set up, create one on the default display
+        currentRepository.getDesktopOrDefault(
+            desktopId,
+            Desktop.newBuilder().setDesktopId(desktopId).setDisplayId(DEFAULT_DISPLAY).build())
+
+    companion object {
+        private const val TAG = "DesktopPersistenceRepo"
+        private const val DESKTOP_REPOSITORIES_DATASTORE_FILE = "desktop_persistent_repositories.pb"
+
+        private const val DEFAULT_USER_ID = 1000
+        private const val DEFAULT_DESKTOP_ID = 0
+
+        object DesktopPersistentRepositoriesSerializer : Serializer<DesktopPersistentRepositories> {
+
+            override val defaultValue: DesktopPersistentRepositories =
+                DesktopPersistentRepositories.getDefaultInstance()
+
+            override suspend fun readFrom(input: InputStream): DesktopPersistentRepositories =
+                try {
+                    DesktopPersistentRepositories.parseFrom(input)
+                } catch (exception: InvalidProtocolBufferException) {
+                    throw CorruptionException("Cannot read proto.", exception)
+                }
+
+            override suspend fun writeTo(t: DesktopPersistentRepositories, output: OutputStream) =
+                t.writeTo(output)
+        }
+
+        private fun Desktop.Builder.updateTaskStates(
+            visibleTasks: ArraySet<Int>,
+            minimizedTasks: ArraySet<Int>
+        ): Desktop.Builder {
+            clearTasksByTaskId()
+            putAllTasksByTaskId(
+                visibleTasks.associateWith {
+                    createDesktopTask(it, state = DesktopTaskState.VISIBLE)
+                })
+            putAllTasksByTaskId(
+                minimizedTasks.associateWith {
+                    createDesktopTask(it, state = DesktopTaskState.MINIMIZED)
+                })
+            return this
+        }
+
+        private fun Desktop.Builder.updateZOrder(
+            freeformTasksInZOrder: ArrayList<Int>
+        ): Desktop.Builder {
+            clearZOrderedTasks()
+            addAllZOrderedTasks(freeformTasksInZOrder)
+            return this
+        }
+
+        private fun createDesktopTask(
+            taskId: Int,
+            state: DesktopTaskState = DesktopTaskState.VISIBLE
+        ): DesktopTask =
+            DesktopTask.newBuilder().setTaskId(taskId).setDesktopTaskState(state).build()
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
new file mode 100644
index 0000000..0105231
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
@@ -0,0 +1,33 @@
+syntax = "proto2";
+
+option java_package = "com.android.wm.shell.desktopmode.persistence";
+option java_multiple_files = true;
+
+// Represents the state of a task in desktop.
+enum DesktopTaskState {
+  VISIBLE = 0;
+  MINIMIZED = 1;
+}
+
+message DesktopTask {
+  optional int32 task_id = 1;
+  optional DesktopTaskState desktop_task_state= 2;
+}
+
+message Desktop {
+  optional int32 display_id = 1;
+  optional int32 desktop_id = 2;
+  // Stores a mapping between task id and the tasks. The key is the task id.
+  map<int32, DesktopTask> tasks_by_task_id = 3;
+  repeated int32 z_ordered_tasks = 4;
+}
+
+message DesktopRepositoryState {
+  // Stores a mapping between a repository and the desktops in it. The key is the desktop id.
+  map<int32, Desktop> desktop = 1;
+}
+
+message DesktopPersistentRepositories {
+  // Stores a mapping between a user and their desktop repository. The key is the user id.
+  map<int32, DesktopRepositoryState> desktop_repo_by_user = 1;
+}
\ No newline at end of file
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 2138acc..cbb08b8 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
@@ -1344,6 +1344,9 @@
         final SurfaceControl leash = pipChange.getLeash();
         final Rect destBounds = mPipOrganizer.getCurrentOrAnimatingBounds();
         final boolean isInPip = mPipTransitionState.isInPip();
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: Update pip for unhandled transition, change=%s, destBounds=%s, isInPip=%b",
+                TAG, pipChange, destBounds, isInPip);
         mSurfaceTransactionHelper
                 .crop(startTransaction, leash, destBounds)
                 .round(startTransaction, leash, isInPip)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 82fbfad..5710af6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -204,8 +204,7 @@
     @NonNull
     @Override
     public Rect getFloatingBoundsOnScreen() {
-        return !mPipBoundsState.getMotionBoundsState().getAnimatingToBounds().isEmpty()
-                ? mPipBoundsState.getMotionBoundsState().getAnimatingToBounds() : getBounds();
+        return getBounds();
     }
 
     @NonNull
@@ -616,7 +615,7 @@
             cancelPhysicsAnimation();
         }
 
-        setAnimatingToBounds(new Rect(
+        mPipBoundsState.getMotionBoundsState().setAnimatingToBounds(new Rect(
                 (int) toX,
                 (int) toY,
                 (int) toX + getBounds().width(),
@@ -660,6 +659,9 @@
             // All motion operations have actually finished.
             mPipBoundsState.setBounds(
                     mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
+            // Notifies the floating coordinator that we moved, so we return these bounds from
+            // {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
+            mFloatingContentCoordinator.onContentMoved(this);
             mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
             if (!mDismissalPending) {
                 // do not schedule resize if PiP is dismissing, which may cause app re-open to
@@ -674,16 +676,6 @@
     }
 
     /**
-     * Notifies the floating coordinator that we're moving, and sets the animating to bounds so
-     * we return these bounds from
-     * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
-     */
-    private void setAnimatingToBounds(Rect bounds) {
-        mPipBoundsState.getMotionBoundsState().setAnimatingToBounds(bounds);
-        mFloatingContentCoordinator.onContentMoved(this);
-    }
-
-    /**
      * Directly resizes the PiP to the given {@param bounds}.
      */
     private void resizePipUnchecked(Rect toBounds) {
@@ -712,7 +704,7 @@
         // This is so all the proper callbacks are performed.
         mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration,
                 TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND, null /* updateBoundsCallback */);
-        setAnimatingToBounds(toBounds);
+        mPipBoundsState.getMotionBoundsState().setAnimatingToBounds(toBounds);
     }
 
     /**
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 a6e25a9..03ff1aa 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
@@ -38,6 +38,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.window.WindowContainerToken;
+import android.window.flags.DesktopModeFlags;
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.NonNull;
@@ -53,11 +54,9 @@
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.recents.IRecentsAnimationRunner;
 import com.android.wm.shell.shared.GroupedRecentTaskInfo;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.shared.split.SplitBounds;
 import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -356,7 +355,7 @@
 
     private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
         if (mListener == null
-                || !DesktopModeFlags.TASK_STACK_OBSERVER_IN_SHELL.isEnabled(mContext)
+                || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
                 || taskInfo.realActivity == null) {
             return;
         }
@@ -370,7 +369,7 @@
     private boolean shouldEnableRunningTasksForDesktopMode() {
         return mPcFeatureEnabled
                 || (DesktopModeStatus.canEnterDesktopMode(mContext)
-                && DesktopModeFlags.TASKBAR_RUNNING_APPS.isEnabled(mContext));
+                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue());
     }
 
     @VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
index 3a0bdb9..e5bfccf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -18,14 +18,13 @@
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.content.Context
 import android.os.IBinder
 import android.util.ArrayMap
 import android.view.SurfaceControl
 import android.view.WindowManager
 import android.window.TransitionInfo
 import com.android.wm.shell.shared.TransitionUtil
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
+import android.window.flags.DesktopModeFlags
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import dagger.Lazy
@@ -38,7 +37,6 @@
  * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it
  */
 class TaskStackTransitionObserver(
-    private val context: Context,
     private val transitions: Lazy<Transitions>,
     shellInit: ShellInit
 ) : Transitions.TransitionObserver {
@@ -64,7 +62,7 @@
         startTransaction: SurfaceControl.Transaction,
         finishTransaction: SurfaceControl.Transaction
     ) {
-        if (DesktopModeFlags.TASK_STACK_OBSERVER_IN_SHELL.isEnabled(context)) {
+        if (DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue) {
             val taskInfoList = mutableListOf<RunningTaskInfo>()
             val transitionTypeList = mutableListOf<Int>()
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
new file mode 100644
index 0000000..a1a9ca9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -0,0 +1,188 @@
+/*
+ * 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.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.TransactionPool;
+
+import java.util.ArrayList;
+
+public class DefaultSurfaceAnimator {
+
+    /** Builds an animator for the surface and adds it to the `animations` list. */
+    static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Animation anim, @NonNull SurfaceControl leash,
+            @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
+            @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
+            @Nullable Rect clipRect, boolean isActivity) {
+        final DefaultAnimationAdapter adapter = new DefaultAnimationAdapter(anim, leash,
+                position, clipRect, cornerRadius, isActivity);
+        buildSurfaceAnimation(animations, anim, finishCallback, pool, mainExecutor, adapter);
+    }
+
+    /** Builds an animator for the surface and adds it to the `animations` list. */
+    static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
+            @NonNull Animation anim, @NonNull Runnable finishCallback,
+            @NonNull TransactionPool pool, @NonNull ShellExecutor mainExecutor,
+            @NonNull AnimationAdapter updateListener) {
+        final SurfaceControl.Transaction transaction = pool.acquire();
+        updateListener.setTransaction(transaction);
+        final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+        // Animation length is already expected to be scaled.
+        va.overrideDurationScale(1.0f);
+        va.setDuration(anim.computeDurationHint());
+        va.addUpdateListener(updateListener);
+        va.addListener(new AnimatorListenerAdapter() {
+            // It is possible for the end/cancel to be called more than once, which may cause
+            // issues if the animating surface has already been released. Track the finished
+            // state here to skip duplicate callbacks. See b/252872225.
+            private boolean mFinished;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                onFinish();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                onFinish();
+            }
+
+            private void onFinish() {
+                if (mFinished) return;
+                mFinished = true;
+                // Apply transformation of end state in case the animation is canceled.
+                if (va.getAnimatedFraction() < 1f) {
+                    va.setCurrentFraction(1f);
+                }
+
+                pool.release(transaction);
+                mainExecutor.execute(() -> {
+                    animations.remove(va);
+                    finishCallback.run();
+                });
+                // The update listener can continue to be called after the animation has ended if
+                // end() is called manually again before the finisher removes the animation.
+                // Remove it manually here to prevent animating a released surface.
+                // See b/252872225.
+                va.removeUpdateListener(updateListener);
+            }
+        });
+        animations.add(va);
+    }
+
+    /** The animation adapter for buildSurfaceAnimation. */
+    abstract static class AnimationAdapter implements ValueAnimator.AnimatorUpdateListener {
+        @NonNull final SurfaceControl mLeash;
+        @NonNull SurfaceControl.Transaction mTransaction;
+        private Choreographer mChoreographer;
+
+        AnimationAdapter(@NonNull SurfaceControl leash) {
+            mLeash = leash;
+        }
+
+        void setTransaction(@NonNull SurfaceControl.Transaction transaction) {
+            mTransaction = transaction;
+        }
+
+        @Override
+        public void onAnimationUpdate(@NonNull ValueAnimator animator) {
+            // The finish callback in buildSurfaceAnimation will ensure that the animation ends
+            // with fraction 1.
+            final long currentPlayTime = animator.getAnimatedFraction() >= 1f
+                    ? animator.getDuration()
+                    : Math.min(animator.getDuration(), animator.getCurrentPlayTime());
+            applyTransformation(animator, currentPlayTime);
+            if (mChoreographer == null) {
+                mChoreographer = Choreographer.getInstance();
+            }
+            mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
+            mTransaction.apply();
+        }
+
+        abstract void applyTransformation(@NonNull ValueAnimator animator, long currentPlayTime);
+    }
+
+    private static class DefaultAnimationAdapter extends AnimationAdapter {
+        final Transformation mTransformation = new Transformation();
+        final float[] mMatrix = new float[9];
+        @NonNull final Animation mAnim;
+        @Nullable final Point mPosition;
+        @Nullable final Rect mClipRect;
+        final float mCornerRadius;
+        final boolean mIsActivity;
+
+        DefaultAnimationAdapter(@NonNull Animation anim, @NonNull SurfaceControl leash,
+                @Nullable Point position, @Nullable Rect clipRect, float cornerRadius,
+                boolean isActivity) {
+            super(leash);
+            mAnim = anim;
+            mPosition = (position != null && (position.x != 0 || position.y != 0))
+                    ? position : null;
+            mClipRect = (clipRect != null && !clipRect.isEmpty()) ? clipRect : null;
+            mCornerRadius = cornerRadius;
+            mIsActivity = isActivity;
+        }
+
+        @Override
+        void applyTransformation(@NonNull ValueAnimator animator, long currentPlayTime) {
+            final Transformation transformation = mTransformation;
+            final SurfaceControl.Transaction t = mTransaction;
+            final SurfaceControl leash = mLeash;
+            transformation.clear();
+            mAnim.getTransformation(currentPlayTime, transformation);
+            if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+                    && mIsActivity && mAnim.getExtensionEdges() != 0) {
+                t.setEdgeExtensionEffect(leash, mAnim.getExtensionEdges());
+            }
+            if (mPosition != null) {
+                transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
+            }
+            t.setMatrix(leash, transformation.getMatrix(), mMatrix);
+            t.setAlpha(leash, transformation.getAlpha());
+
+            if (mClipRect != null) {
+                Rect clipRect = mClipRect;
+                final Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
+                if (!extensionInsets.equals(Insets.NONE)) {
+                    // Clip out any overflowing edge extension.
+                    clipRect = new Rect(mClipRect);
+                    clipRect.inset(extensionInsets);
+                    t.setCrop(leash, clipRect);
+                }
+                if (mCornerRadius > 0 && mAnim.hasRoundedCorners()) {
+                    // Rounded corner can only be applied if a crop is set.
+                    t.setCrop(leash, clipRect);
+                    t.setCornerRadius(leash, mCornerRadius);
+                }
+            }
+        }
+    }
+}
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 1573291..d3bed59 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
@@ -358,12 +358,9 @@
 
             if (mode == TRANSIT_CHANGE && change.hasFlags(FLAG_IS_DISPLAY)) {
                 if (info.getType() == TRANSIT_CHANGE) {
-                    int anim = getRotationAnimationHint(change, info, mDisplayController);
+                    final int anim = getRotationAnimationHint(change, info, mDisplayController);
                     isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS;
                     if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
-                        if (wallpaperTransit != WALLPAPER_TRANSITION_NONE) {
-                            anim |= ScreenRotationAnimation.ANIMATION_HINT_HAS_WALLPAPER;
-                        }
                         startRotationAnimation(startTransaction, change, info, anim, animations,
                                 onAnimFinish);
                         isDisplayRotationAnimationStarted = true;
@@ -829,26 +826,29 @@
             @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
             @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
             @Nullable Rect clipRect, boolean isActivity) {
-        final DefaultAnimationAdapter adapter = new DefaultAnimationAdapter(anim, leash,
-                position, clipRect, cornerRadius, isActivity);
-        buildSurfaceAnimation(animations, anim, finishCallback, pool, mainExecutor, adapter);
-    }
-
-    /** Builds an animator for the surface and adds it to the `animations` list. */
-    static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations,
-            @NonNull Animation anim, @NonNull Runnable finishCallback,
-            @NonNull TransactionPool pool, @NonNull ShellExecutor mainExecutor,
-            @NonNull AnimationAdapter updateListener) {
+        if (Flags.commonSurfaceAnimator()) {
+            DefaultSurfaceAnimator.buildSurfaceAnimation(animations, anim, leash, finishCallback,
+                    pool, mainExecutor, position, cornerRadius, clipRect, isActivity);
+            return;
+        }
         final SurfaceControl.Transaction transaction = pool.acquire();
-        updateListener.setTransaction(transaction);
         final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+        final Transformation transformation = new Transformation();
+        final float[] matrix = new float[9];
         // Animation length is already expected to be scaled.
         va.overrideDurationScale(1.0f);
         va.setDuration(anim.computeDurationHint());
+        final ValueAnimator.AnimatorUpdateListener updateListener = animation -> {
+            final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
+
+            applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
+                    position, cornerRadius, clipRect, isActivity);
+        };
         va.addUpdateListener(updateListener);
 
         final Runnable finisher = () -> {
-            updateListener.onAnimationUpdate(va);
+            applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
+                    position, cornerRadius, clipRect, isActivity);
 
             pool.release(transaction);
             mainExecutor.execute(() -> {
@@ -1012,88 +1012,37 @@
                 || animType == ANIM_FROM_STYLE;
     }
 
-    /** The animation adapter for buildSurfaceAnimation. */
-    abstract static class AnimationAdapter implements ValueAnimator.AnimatorUpdateListener {
-        @NonNull final SurfaceControl mLeash;
-        @NonNull SurfaceControl.Transaction mTransaction;
-        private Choreographer mChoreographer;
+    private static void applyTransformation(long time, SurfaceControl.Transaction t,
+            SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
+            Point position, float cornerRadius, @Nullable Rect immutableClipRect,
+            boolean isActivity) {
+        tmpTransformation.clear();
+        anim.getTransformation(time, tmpTransformation);
+        if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+                && anim.getExtensionEdges() != 0x0 && isActivity) {
+            t.setEdgeExtensionEffect(leash, anim.getExtensionEdges());
+        }
+        if (position != null) {
+            tmpTransformation.getMatrix().postTranslate(position.x, position.y);
+        }
+        t.setMatrix(leash, tmpTransformation.getMatrix(), matrix);
+        t.setAlpha(leash, tmpTransformation.getAlpha());
 
-        AnimationAdapter(@NonNull SurfaceControl leash) {
-            mLeash = leash;
+        final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect);
+        Insets extensionInsets = Insets.min(tmpTransformation.getInsets(), Insets.NONE);
+        if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
+            // Clip out any overflowing edge extension
+            clipRect.inset(extensionInsets);
+            t.setCrop(leash, clipRect);
         }
 
-        void setTransaction(@NonNull SurfaceControl.Transaction transaction) {
-            mTransaction = transaction;
+        if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
+            // We can only apply rounded corner if a crop is set
+            t.setCrop(leash, clipRect);
+            t.setCornerRadius(leash, cornerRadius);
         }
 
-        @Override
-        public void onAnimationUpdate(@NonNull ValueAnimator animator) {
-            applyTransformation(animator);
-            if (mChoreographer == null) {
-                mChoreographer = Choreographer.getInstance();
-            }
-            mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
-            mTransaction.apply();
-        }
-
-        abstract void applyTransformation(@NonNull ValueAnimator animator);
-    }
-
-    private static class DefaultAnimationAdapter extends AnimationAdapter {
-        final Transformation mTransformation = new Transformation();
-        final float[] mMatrix = new float[9];
-        @NonNull final Animation mAnim;
-        @Nullable final Point mPosition;
-        @Nullable final Rect mClipRect;
-        final float mCornerRadius;
-        final boolean mIsActivity;
-
-        DefaultAnimationAdapter(@NonNull Animation anim, @NonNull SurfaceControl leash,
-                @Nullable Point position, @Nullable Rect clipRect, float cornerRadius,
-                boolean isActivity) {
-            super(leash);
-            mAnim = anim;
-            mPosition = (position != null && (position.x != 0 || position.y != 0))
-                    ? position : null;
-            mClipRect = (clipRect != null && !clipRect.isEmpty()) ? clipRect : null;
-            mCornerRadius = cornerRadius;
-            mIsActivity = isActivity;
-        }
-
-        @Override
-        void applyTransformation(@NonNull ValueAnimator animator) {
-            final long currentPlayTime = Math.min(animator.getDuration(),
-                    animator.getCurrentPlayTime());
-            final Transformation transformation = mTransformation;
-            final SurfaceControl.Transaction t = mTransaction;
-            final SurfaceControl leash = mLeash;
-            transformation.clear();
-            mAnim.getTransformation(currentPlayTime, transformation);
-            if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
-                    && mIsActivity && mAnim.getExtensionEdges() != 0) {
-                t.setEdgeExtensionEffect(leash, mAnim.getExtensionEdges());
-            }
-            if (mPosition != null) {
-                transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
-            }
-            t.setMatrix(leash, transformation.getMatrix(), mMatrix);
-            t.setAlpha(leash, transformation.getAlpha());
-
-            if (mClipRect != null) {
-                Rect clipRect = mClipRect;
-                final Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
-                if (!extensionInsets.equals(Insets.NONE)) {
-                    // Clip out any overflowing edge extension.
-                    clipRect = new Rect(mClipRect);
-                    clipRect.inset(extensionInsets);
-                    t.setCrop(leash, clipRect);
-                }
-                if (mCornerRadius > 0 && mAnim.hasRoundedCorners()) {
-                    // Rounded corner can only be applied if a crop is set.
-                    t.setCrop(leash, clipRect);
-                    t.setCornerRadius(leash, mCornerRadius);
-                }
-            }
-        }
+        t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+        t.apply();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
new file mode 100644
index 0000000..2f5059f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/FocusTransitionObserver.java
@@ -0,0 +1,142 @@
+/*
+ * 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.transition;
+
+import static android.view.Display.INVALID_DISPLAY;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
+
+import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
+import static com.android.wm.shell.transition.Transitions.TransitionObserver;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+
+import com.android.wm.shell.shared.FocusTransitionListener;
+import com.android.wm.shell.shared.IFocusTransitionListener;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * The {@link TransitionObserver} that observes for transitions involving focus switch.
+ * It reports transitions to callers outside of the process via {@link IFocusTransitionListener},
+ * and callers within the process via {@link FocusTransitionListener}.
+ */
+public class FocusTransitionObserver implements TransitionObserver {
+    private static final String TAG = FocusTransitionObserver.class.getSimpleName();
+
+    private IFocusTransitionListener mRemoteListener;
+    private final Map<FocusTransitionListener, Executor> mLocalListeners =
+            new HashMap<>();
+
+    private int mFocusedDisplayId = INVALID_DISPLAY;
+
+    public FocusTransitionObserver() {}
+
+    @Override
+    public void onTransitionReady(@NonNull IBinder transition,
+            @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction) {
+        final List<TransitionInfo.Change> changes = info.getChanges();
+        for (int i = changes.size() - 1; i >= 0; i--) {
+            final TransitionInfo.Change change = changes.get(i);
+            final RunningTaskInfo task = change.getTaskInfo();
+            if (task != null && task.isFocused && change.hasFlags(FLAG_MOVED_TO_TOP)) {
+                if (mFocusedDisplayId != task.displayId) {
+                    mFocusedDisplayId = task.displayId;
+                    notifyFocusedDisplayChanged();
+                }
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void onTransitionStarting(@NonNull IBinder transition) {}
+
+    @Override
+    public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {}
+
+    @Override
+    public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {}
+
+    /**
+     * Sets the focus transition listener that receives any transitions resulting in focus switch.
+     * This is for calls from outside the Shell, within the host process.
+     *
+     */
+    public void setLocalFocusTransitionListener(FocusTransitionListener listener,
+            Executor executor) {
+        if (!enableDisplayFocusInShellTransitions()) {
+            return;
+        }
+        mLocalListeners.put(listener, executor);
+        executor.execute(() -> listener.onFocusedDisplayChanged(mFocusedDisplayId));
+    }
+
+    /**
+     * Sets the focus transition listener that receives any transitions resulting in focus switch.
+     * This is for calls from outside the Shell, within the host process.
+     *
+     */
+    public void unsetLocalFocusTransitionListener(FocusTransitionListener listener) {
+        if (!enableDisplayFocusInShellTransitions()) {
+            return;
+        }
+        mLocalListeners.remove(listener);
+    }
+
+    /**
+     * Sets the focus transition listener that receives any transitions resulting in focus switch.
+     * This is for calls from outside the host process.
+     */
+    public void setRemoteFocusTransitionListener(Transitions transitions,
+            IFocusTransitionListener listener) {
+        if (!enableDisplayFocusInShellTransitions()) {
+            return;
+        }
+        mRemoteListener = listener;
+        notifyFocusedDisplayChangedToRemote();
+    }
+
+    /**
+     * Notifies the listener that display focus has changed.
+     */
+    public void notifyFocusedDisplayChanged() {
+        notifyFocusedDisplayChangedToRemote();
+        mLocalListeners.forEach((listener, executor) ->
+                executor.execute(() -> listener.onFocusedDisplayChanged(mFocusedDisplayId)));
+    }
+
+    private void notifyFocusedDisplayChangedToRemote() {
+        if (mRemoteListener != null) {
+            try {
+                mRemoteListener.onFocusedDisplayChanged(mFocusedDisplayId);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed call notifyFocusedDisplayChangedToRemote", e);
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index b9d11a3..5802e2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -25,9 +25,12 @@
 import static com.android.wm.shell.transition.Transitions.TAG;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
@@ -35,7 +38,6 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.window.ScreenCapture;
@@ -72,9 +74,6 @@
  */
 class ScreenRotationAnimation {
     static final int MAX_ANIMATION_DURATION = 10 * 1000;
-    static final int ANIMATION_HINT_HAS_WALLPAPER = 1 << 8;
-    /** It must cover all WindowManager#ROTATION_ANIMATION_*. */
-    private static final int ANIMATION_TYPE_MASK = 0xff;
 
     private final Context mContext;
     private final TransactionPool mTransactionPool;
@@ -82,7 +81,7 @@
     /** The leash of the changing window container. */
     private final SurfaceControl mSurfaceControl;
 
-    private final int mAnimType;
+    private final int mAnimHint;
     private final int mStartWidth;
     private final int mStartHeight;
     private final int mEndWidth;
@@ -99,12 +98,6 @@
     private SurfaceControl mBackColorSurface;
     /** The leash using to animate screenshot layer. */
     private final SurfaceControl mAnimLeash;
-    /**
-     * The container with background color for {@link #mSurfaceControl}. It is only created if
-     * {@link #mSurfaceControl} may be translucent. E.g. visible wallpaper with alpha < 1 (dimmed).
-     * That prevents flickering of alpha blending.
-     */
-    private SurfaceControl mBackEffectSurface;
 
     // The current active animation to move from the old to the new rotated
     // state.  Which animation is run here will depend on the old and new
@@ -122,7 +115,7 @@
             Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash, int animHint) {
         mContext = context;
         mTransactionPool = pool;
-        mAnimType = animHint & ANIMATION_TYPE_MASK;
+        mAnimHint = animHint;
 
         mSurfaceControl = change.getLeash();
         mStartWidth = change.getStartAbsBounds().width();
@@ -177,20 +170,11 @@
                 }
                 hardwareBuffer.close();
             }
-            if ((animHint & ANIMATION_HINT_HAS_WALLPAPER) != 0) {
-                mBackEffectSurface = new SurfaceControl.Builder()
-                        .setCallsite("ShellRotationAnimation").setParent(rootLeash)
-                        .setEffectLayer().setOpaque(true).setName("BackEffect").build();
-                t.reparent(mSurfaceControl, mBackEffectSurface)
-                        .setColor(mBackEffectSurface,
-                                new float[] {mStartLuma, mStartLuma, mStartLuma})
-                        .show(mBackEffectSurface);
-            }
 
             t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
             t.show(mAnimLeash);
             // Crop the real content in case it contains a larger child layer, e.g. wallpaper.
-            t.setCrop(getEnterSurface(), new Rect(0, 0, mEndWidth, mEndHeight));
+            t.setCrop(mSurfaceControl, new Rect(0, 0, mEndWidth, mEndHeight));
 
             if (!isCustomRotate()) {
                 mBackColorSurface = new SurfaceControl.Builder()
@@ -215,12 +199,7 @@
     }
 
     private boolean isCustomRotate() {
-        return mAnimType == ROTATION_ANIMATION_CROSSFADE || mAnimType == ROTATION_ANIMATION_JUMPCUT;
-    }
-
-    /** Returns the surface which contains the real content to animate enter. */
-    private SurfaceControl getEnterSurface() {
-        return mBackEffectSurface != null ? mBackEffectSurface : mSurfaceControl;
+        return mAnimHint == ROTATION_ANIMATION_CROSSFADE || mAnimHint == ROTATION_ANIMATION_JUMPCUT;
     }
 
     private void setScreenshotTransform(SurfaceControl.Transaction t) {
@@ -281,7 +260,7 @@
         final boolean customRotate = isCustomRotate();
         if (customRotate) {
             mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
-                    mAnimType == ROTATION_ANIMATION_JUMPCUT ? R.anim.rotation_animation_jump_exit
+                    mAnimHint == ROTATION_ANIMATION_JUMPCUT ? R.anim.rotation_animation_jump_exit
                             : R.anim.rotation_animation_xfade_exit);
             mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
                     R.anim.rotation_animation_enter);
@@ -335,11 +314,7 @@
         } else {
             startDisplayRotation(animations, finishCallback, mainExecutor);
             startScreenshotRotationAnimation(animations, finishCallback, mainExecutor);
-            if (mBackEffectSurface != null && mStartLuma > 0.1f) {
-                // Animate from the color of background to black for smooth alpha blending.
-                buildLumaAnimation(animations, mStartLuma, 0f /* endLuma */, mBackEffectSurface,
-                        animationScale, finishCallback, mainExecutor);
-            }
+            //startColorAnimation(mTransaction, animationScale);
         }
 
         return true;
@@ -347,7 +322,7 @@
 
     private void startDisplayRotation(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
-        buildSurfaceAnimation(animations, mRotateEnterAnimation, getEnterSurface(), finishCallback,
+        buildSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
                 mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
                 null /* clipRect */, false /* isActivity */);
     }
@@ -366,17 +341,40 @@
                 null /* clipRect */, false /* isActivity */);
     }
 
-    private void buildLumaAnimation(@NonNull ArrayList<Animator> animations,
-            float startLuma, float endLuma, SurfaceControl surface, float animationScale,
-            @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
-        final long durationMillis = (long) (mContext.getResources().getInteger(
-                R.integer.config_screen_rotation_color_transition) * animationScale);
-        final LumaAnimation animation = new LumaAnimation(durationMillis);
-        // Align the end with the enter animation.
-        animation.setStartOffset(mRotateEnterAnimation.getDuration() - durationMillis);
-        final LumaAnimationAdapter adapter = new LumaAnimationAdapter(surface, startLuma, endLuma);
-        buildSurfaceAnimation(animations, animation, finishCallback, mTransactionPool,
-                mainExecutor, adapter);
+    private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
+        int colorTransitionMs = mContext.getResources().getInteger(
+                R.integer.config_screen_rotation_color_transition);
+        final float[] rgbTmpFloat = new float[3];
+        final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
+        final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
+        final long duration = colorTransitionMs * (long) animationScale;
+        final Transaction t = mTransactionPool.acquire();
+
+        final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+        // Animation length is already expected to be scaled.
+        va.overrideDurationScale(1.0f);
+        va.setDuration(duration);
+        va.addUpdateListener(animation -> {
+            final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
+            final float fraction = currentPlayTime / va.getDuration();
+            applyColor(startColor, endColor, rgbTmpFloat, fraction, mBackColorSurface, t);
+        });
+        va.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+                        t);
+                mTransactionPool.release(t);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+                        t);
+                mTransactionPool.release(t);
+            }
+        });
+        animExecutor.execute(va::start);
     }
 
     public void kill() {
@@ -391,47 +389,21 @@
         if (mBackColorSurface != null && mBackColorSurface.isValid()) {
             t.remove(mBackColorSurface);
         }
-        if (mBackEffectSurface != null && mBackEffectSurface.isValid()) {
-            t.remove(mBackEffectSurface);
-        }
         t.apply();
         mTransactionPool.release(t);
     }
 
-    /** A no-op wrapper to provide animation duration. */
-    private static class LumaAnimation extends Animation {
-        LumaAnimation(long durationMillis) {
-            setDuration(durationMillis);
+    private static void applyColor(int startColor, int endColor, float[] rgbFloat,
+            float fraction, SurfaceControl surface, SurfaceControl.Transaction t) {
+        final int color = (Integer) ArgbEvaluator.getInstance().evaluate(fraction, startColor,
+                endColor);
+        Color middleColor = Color.valueOf(color);
+        rgbFloat[0] = middleColor.red();
+        rgbFloat[1] = middleColor.green();
+        rgbFloat[2] = middleColor.blue();
+        if (surface.isValid()) {
+            t.setColor(surface, rgbFloat);
         }
-    }
-
-    private static class LumaAnimationAdapter extends DefaultTransitionHandler.AnimationAdapter {
-        final float[] mColorArray = new float[3];
-        final float mStartLuma;
-        final float mEndLuma;
-        final AccelerateInterpolator mInterpolation;
-
-        LumaAnimationAdapter(@NonNull SurfaceControl leash, float startLuma, float endLuma) {
-            super(leash);
-            mStartLuma = startLuma;
-            mEndLuma = endLuma;
-            // Make the initial progress color lighter if the background is light. That avoids
-            // darker content when fading into the entering surface.
-            final float factor = Math.min(3f, (Math.max(0.5f, mStartLuma) - 0.5f) * 10);
-            Slog.d(TAG, "Luma=" + mStartLuma + " factor=" + factor);
-            mInterpolation = factor > 0.5f ? new AccelerateInterpolator(factor) : null;
-        }
-
-        @Override
-        void applyTransformation(ValueAnimator animator) {
-            final float fraction = mInterpolation != null
-                    ? mInterpolation.getInterpolation(animator.getAnimatedFraction())
-                    : animator.getAnimatedFraction();
-            final float luma = mStartLuma + fraction * (mEndLuma - mStartLuma);
-            mColorArray[0] = luma;
-            mColorArray[1] = luma;
-            mColorArray[2] = luma;
-            mTransaction.setColor(mLeash, mColorArray);
-        }
+        t.apply();
     }
 }
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 d03832d..d280dcd 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
@@ -87,6 +87,8 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.FocusTransitionListener;
+import com.android.wm.shell.shared.IFocusTransitionListener;
 import com.android.wm.shell.shared.IHomeTransitionListener;
 import com.android.wm.shell.shared.IShellTransitions;
 import com.android.wm.shell.shared.ShellTransitions;
@@ -103,6 +105,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.concurrent.Executor;
 
 /**
  * Plays transition animations. Within this player, each transition has a lifecycle.
@@ -224,6 +227,7 @@
     private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
 
     private HomeTransitionObserver mHomeTransitionObserver;
+    private FocusTransitionObserver mFocusTransitionObserver;
 
     /** List of {@link Runnable} instances to run when the last active transition has finished.  */
     private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>();
@@ -309,10 +313,12 @@
             @NonNull ShellExecutor mainExecutor,
             @NonNull Handler mainHandler,
             @NonNull ShellExecutor animExecutor,
-            @NonNull HomeTransitionObserver observer) {
+            @NonNull HomeTransitionObserver homeTransitionObserver,
+            @NonNull FocusTransitionObserver focusTransitionObserver) {
         this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool,
                 displayController, mainExecutor, mainHandler, animExecutor,
-                new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit), observer);
+                new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit),
+                homeTransitionObserver, focusTransitionObserver);
     }
 
     public Transitions(@NonNull Context context,
@@ -326,7 +332,8 @@
             @NonNull Handler mainHandler,
             @NonNull ShellExecutor animExecutor,
             @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer,
-            @NonNull HomeTransitionObserver observer) {
+            @NonNull HomeTransitionObserver homeTransitionObserver,
+            @NonNull FocusTransitionObserver focusTransitionObserver) {
         mOrganizer = organizer;
         mContext = context;
         mMainExecutor = mainExecutor;
@@ -345,7 +352,8 @@
         mHandlers.add(mRemoteTransitionHandler);
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
         shellInit.addInitCallback(this::onInit, this);
-        mHomeTransitionObserver = observer;
+        mHomeTransitionObserver = homeTransitionObserver;
+        mFocusTransitionObserver = focusTransitionObserver;
 
         if (android.tracing.Flags.perfettoTransitionTracing()) {
             mTransitionTracer = new PerfettoTransitionTracer();
@@ -384,6 +392,8 @@
 
         mShellCommandHandler.addCommandCallback("transitions", this, this);
         mShellCommandHandler.addDumpCallback(this::dump, this);
+
+        registerObserver(mFocusTransitionObserver);
     }
 
     public boolean isRegistered() {
@@ -1573,6 +1583,21 @@
             mMainExecutor.execute(
                     () -> mRemoteTransitionHandler.removeFiltered(remoteTransition));
         }
+
+        @Override
+        public void setFocusTransitionListener(FocusTransitionListener listener,
+                Executor executor) {
+            mMainExecutor.execute(() ->
+                    mFocusTransitionObserver.setLocalFocusTransitionListener(listener, executor));
+
+        }
+
+        @Override
+        public void unsetFocusTransitionListener(FocusTransitionListener listener) {
+            mMainExecutor.execute(() ->
+                    mFocusTransitionObserver.unsetLocalFocusTransitionListener(listener));
+
+        }
     }
 
     /**
@@ -1634,6 +1659,15 @@
         }
 
         @Override
+        public void setFocusTransitionListener(IFocusTransitionListener listener) {
+            executeRemoteCallWithTaskPermission(mTransitions, "setFocusTransitionListener",
+                    (transitions) -> {
+                        transitions.mFocusTransitionObserver.setRemoteFocusTransitionListener(
+                                transitions, listener);
+                    });
+        }
+
+        @Override
         public SurfaceControl getHomeTaskOverlayContainer() {
             SurfaceControl[] result = new SurfaceControl[1];
             executeRemoteCallWithTaskPermission(mTransitions, "getHomeTaskOverlayContainer",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 0151395..f5b2340 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -44,6 +44,7 @@
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.window.DisplayAreaInfo;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
@@ -62,7 +63,6 @@
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 /**
  * View model for the window decoration with a caption and shadows. Works with
@@ -84,7 +84,6 @@
     private final Transitions mTransitions;
     private final Region mExclusionRegion = Region.obtain();
     private final InputManager mInputManager;
-    private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier;
     private TaskOperations mTaskOperations;
 
     /**
@@ -122,8 +121,7 @@
             DisplayController displayController,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             SyncTransactionQueue syncQueue,
-            Transitions transitions,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            Transitions transitions) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
@@ -135,7 +133,6 @@
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mSyncQueue = syncQueue;
         mTransitions = transitions;
-        mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
         if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
             mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
         }
@@ -299,8 +296,7 @@
                         mMainHandler,
                         mBgExecutor,
                         mMainChoreographer,
-                        mSyncQueue,
-                        mWindowDecorViewHostSupplier);
+                        mSyncQueue);
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final FluidResizeTaskPositioner taskPositioner =
@@ -310,7 +306,6 @@
                 new CaptionTouchEventListener(taskInfo, taskPositioner);
         windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
         windowDecoration.setDragPositioningCallback(taskPositioner);
-        windowDecoration.setDragDetector(touchEventListener.mDragDetector);
         windowDecoration.setTaskDragResizer(taskPositioner);
         windowDecoration.relayout(taskInfo, startT, finishT,
                 false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */);
@@ -334,7 +329,8 @@
             mTaskId = taskInfo.taskId;
             mTaskToken = taskInfo.token;
             mDragPositioningCallback = dragPositioningCallback;
-            mDragDetector = new DragDetector(this);
+            mDragDetector = new DragDetector(this, 0 /* holdToDragMinDurationMs */,
+                    ViewConfiguration.get(mContext).getScaledTouchSlop());
             mDisplayId = taskInfo.displayId;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 0caa8e9..05065be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.window.flags.DesktopModeFlags.ENABLE_WINDOWING_SCALED_RESIZING;
+
 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;
@@ -55,9 +57,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 /**
  * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -74,7 +74,6 @@
     private View.OnTouchListener mOnCaptionTouchListener;
     private DragPositioningCallback mDragPositioningCallback;
     private DragResizeInputListener mDragResizeListener;
-    private DragDetector mDragDetector;
 
     private RelayoutParams mRelayoutParams = new RelayoutParams();
     private final RelayoutResult<WindowDecorLinearLayout> mResult =
@@ -90,10 +89,8 @@
             Handler handler,
             @ShellBackgroundThread ShellExecutor bgExecutor,
             Choreographer choreographer,
-            SyncTransactionQueue syncQueue,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
-        super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
-                windowDecorViewHostSupplier);
+            SyncTransactionQueue syncQueue) {
+        super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface);
         mHandler = handler;
         mBgExecutor = bgExecutor;
         mChoreographer = choreographer;
@@ -176,12 +173,6 @@
         return stableBounds.bottom - requiredEmptySpace;
     }
 
-
-    void setDragDetector(DragDetector dragDetector) {
-        mDragDetector = dragDetector;
-        mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
-    }
-
     @Override
     void relayout(RunningTaskInfo taskInfo) {
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -241,7 +232,7 @@
             boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition) {
         final boolean isFreeform =
                 taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
-        final boolean isDragResizeable = DesktopModeFlags.SCALED_RESIZING.isEnabled(mContext)
+        final boolean isDragResizeable = ENABLE_WINDOWING_SCALED_RESIZING.isTrue()
                 ? isFreeform : isFreeform && taskInfo.isResizeable;
 
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
@@ -288,11 +279,10 @@
 
         final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
                 .getScaledTouchSlop();
-        mDragDetector.setTouchSlop(touchSlop);
 
         final Resources res = mResult.mRootView.getResources();
         mDragResizeListener.setGeometry(new DragResizeWindowGeometry(0 /* taskCornerRadius */,
-                new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(mContext, res),
+                new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
                 getResizeHandleEdgeInset(res), getFineResizeCornerSize(res),
                 getLargeResizeCornerSize(res)), touchSlop);
     }
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 7ea0bd6..272508f 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
@@ -79,10 +79,12 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.widget.Toast;
 import android.window.TaskSnapshot;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
+import android.window.flags.DesktopModeFlags;
 
 import androidx.annotation.Nullable;
 
@@ -113,7 +115,6 @@
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 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;
@@ -129,7 +130,6 @@
 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import kotlin.Pair;
 import kotlin.Unit;
@@ -168,7 +168,6 @@
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter;
     private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
-    private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -238,8 +237,7 @@
             MultiInstanceHelper multiInstanceHelper,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) {
         this(
                 context,
                 shellExecutor,
@@ -259,7 +257,6 @@
                 genericLinksParser,
                 assistContentRequester,
                 multiInstanceHelper,
-                windowDecorViewHostSupplier,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
@@ -293,7 +290,6 @@
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
             MultiInstanceHelper multiInstanceHelper,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
@@ -321,7 +317,6 @@
         mMultiInstanceHelper = multiInstanceHelper;
         mShellCommandHandler = shellCommandHandler;
         mWindowManager = windowManager;
-        mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
@@ -520,7 +515,7 @@
         }
 
         if (!decoration.mTaskInfo.isResizeable
-                && DesktopModeFlags.DISABLE_SNAP_RESIZE.isEnabled(mContext)) {
+                && DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()) {
             Toast.makeText(mContext,
                     R.string.desktop_mode_non_resizable_snap_text, Toast.LENGTH_SHORT).show();
         } else {
@@ -663,11 +658,14 @@
     private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
             implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
             View.OnGenericMotionListener, DragDetector.MotionEventHandler {
+        private static final long APP_HANDLE_HOLD_TO_DRAG_DURATION_MS = 100;
+        private static final long APP_HEADER_HOLD_TO_DRAG_DURATION_MS = 0;
 
         private final int mTaskId;
         private final WindowContainerToken mTaskToken;
         private final DragPositioningCallback mDragPositioningCallback;
-        private final DragDetector mDragDetector;
+        private final DragDetector mHandleDragDetector;
+        private final DragDetector mHeaderDragDetector;
         private final GestureDetector mGestureDetector;
         private final int mDisplayId;
         private final Rect mOnDragStartInitialBounds = new Rect();
@@ -689,7 +687,13 @@
             mTaskId = taskInfo.taskId;
             mTaskToken = taskInfo.token;
             mDragPositioningCallback = dragPositioningCallback;
-            mDragDetector = new DragDetector(this);
+            final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+            final long appHandleHoldToDragDuration = Flags.enableHoldToDragAppHandle()
+                    ? APP_HANDLE_HOLD_TO_DRAG_DURATION_MS : 0;
+            mHandleDragDetector = new DragDetector(this, appHandleHoldToDragDuration,
+                    touchSlop);
+            mHeaderDragDetector = new DragDetector(this, APP_HEADER_HOLD_TO_DRAG_DURATION_MS,
+                    touchSlop);
             mGestureDetector = new GestureDetector(mContext, this);
             mDisplayId = taskInfo.displayId;
         }
@@ -748,7 +752,7 @@
                     && id != R.id.maximize_window && id != R.id.minimize_window) {
                 return false;
             }
-
+            final boolean isAppHandle = !getTaskInfo().isFreeform();
             final int actionMasked = e.getActionMasked();
             final boolean isDown = actionMasked == MotionEvent.ACTION_DOWN;
             final boolean isUpOrCancel = actionMasked == MotionEvent.ACTION_CANCEL
@@ -797,7 +801,11 @@
                 // Gesture is finished, reset state.
                 mShouldPilferCaptionEvents = false;
             }
-            return mDragDetector.onMotionEvent(v, e);
+            if (isAppHandle) {
+                return mHandleDragDetector.onMotionEvent(v, e);
+            } else {
+                return mHeaderDragDetector.onMotionEvent(v, e);
+            }
         }
 
         @Override
@@ -865,6 +873,12 @@
             }
         }
 
+        @NonNull
+        private RunningTaskInfo getTaskInfo() {
+            final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+            return decoration.mTaskInfo;
+        }
+
         private boolean handleNonFreeformMotionEvent(DesktopModeWindowDecoration decoration,
                 View v, MotionEvent e) {
             final int id = v.getId();
@@ -1353,7 +1367,7 @@
                 && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) {
             return false;
         }
-        if (DesktopModeFlags.MODALS_POLICY.isEnabled(mContext)
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
                 && isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) {
             return false;
         }
@@ -1392,8 +1406,7 @@
                         mGenericLinksParser,
                         mAssistContentRequester,
                         mMultiInstanceHelper,
-                        mWindowDecorCaptionHandleRepository,
-                        mWindowDecorViewHostSupplier);
+                        mWindowDecorCaptionHandleRepository);
         mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
 
         final TaskPositioner taskPositioner = mTaskPositionerFactory.create(
@@ -1447,7 +1460,6 @@
                 touchEventListener, touchEventListener, touchEventListener, touchEventListener);
         windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
         windowDecoration.setDragPositioningCallback(taskPositioner);
-        windowDecoration.setDragDetector(touchEventListener.mDragDetector);
         windowDecoration.relayout(taskInfo, startT, finishT,
                 false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */);
         if (!Flags.enableHandleInputFix()) {
@@ -1647,7 +1659,7 @@
                             dragStartListener,
                             transactionFactory);
 
-            if (DesktopModeFlags.SCALED_RESIZING.isEnabled(windowDecoration.mContext)) {
+            if (DesktopModeFlags.ENABLE_WINDOWING_SCALED_RESIZING.isTrue()) {
                 return new FixedAspectRatioTaskPositionerDecorator(windowDecoration,
                         taskPositioner);
             }
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 10e4a39..8a53f5b 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
@@ -38,6 +38,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.assist.AssistContent;
@@ -69,6 +70,7 @@
 import android.widget.ImageButton;
 import android.window.TaskSnapshot;
 import android.window.WindowContainerTransaction;
+import android.window.flags.DesktopModeFlags;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -89,7 +91,6 @@
 import com.android.wm.shell.desktopmode.CaptionState;
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer;
@@ -98,7 +99,6 @@
 import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
 import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import kotlin.Pair;
 import kotlin.Unit;
@@ -144,10 +144,12 @@
     private Function0<Unit> mOnManageWindowsClickListener;
     private DragPositioningCallback mDragPositioningCallback;
     private DragResizeInputListener mDragResizeListener;
-    private DragDetector mDragDetector;
+    private Runnable mCurrentViewHostRunnable = null;
     private RelayoutParams mRelayoutParams = new RelayoutParams();
     private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
             new WindowDecoration.RelayoutResult<>();
+    private final Runnable mViewHostRunnable =
+            () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult);
 
     private final Point mPositionInParent = new Point();
     private HandleMenu mHandleMenu;
@@ -204,8 +206,7 @@
             AppToWebGenericLinksParser genericLinksParser,
             AssistContentRequester assistContentRequester,
             MultiInstanceHelper multiInstanceHelper,
-            WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) {
         this (context, userContext, displayController, splitScreenController, taskOrganizer,
                 taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
                 appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
@@ -213,7 +214,7 @@
                 SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
                 WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
                         context.getSystemService(WindowManager.class)),
-                new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier,
+                new SurfaceControlViewHostFactory() {},
                 DefaultMaximizeMenuFactory.INSTANCE,
                 DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
                 windowDecorCaptionHandleRepository);
@@ -241,7 +242,6 @@
             Supplier<SurfaceControl> surfaceControlSupplier,
             WindowManagerWrapper windowManagerWrapper,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory,
-            WindowDecorViewHostSupplier windowDecorViewHostSupplier,
             MaximizeMenuFactory maximizeMenuFactory,
             HandleMenuFactory handleMenuFactory,
             MultiInstanceHelper multiInstanceHelper,
@@ -249,7 +249,7 @@
         super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                 surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                 windowContainerTransactionSupplier, surfaceControlSupplier,
-                surfaceControlViewHostFactory, windowDecorViewHostSupplier);
+                surfaceControlViewHostFactory);
         mSplitScreenController = splitScreenController;
         mHandler = handler;
         mBgExecutor = bgExecutor;
@@ -335,11 +335,6 @@
         mDragPositioningCallback = dragPositioningCallback;
     }
 
-    void setDragDetector(DragDetector dragDetector) {
-        mDragDetector = dragDetector;
-        mDragDetector.setTouchSlop(ViewConfiguration.get(mContext).getScaledTouchSlop());
-    }
-
     void setOpenInBrowserClickListener(Consumer<Uri> listener) {
         mOpenInBrowserClickListener = listener;
     }
@@ -368,6 +363,73 @@
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
+        if (taskInfo.isFreeform()) {
+            // The Task is in Freeform mode -> show its header in sync since it's an integral part
+            // of the window itself - a delayed header might cause bad UX.
+            relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+                    shouldSetTaskPositionAndCrop);
+        } else {
+            // The Task is outside Freeform mode -> allow the handle view to be delayed since the
+            // handle is just a small addition to the window.
+            relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+                    shouldSetTaskPositionAndCrop);
+        }
+        Trace.endSection();
+    }
+
+    /** Run the whole relayout phase immediately without delay. */
+    private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+        // Clear the current ViewHost runnable as we will update the ViewHost here
+        clearCurrentViewHostRunnable();
+        updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+                shouldSetTaskPositionAndCrop);
+        if (mResult.mRootView != null) {
+            updateViewHost(mRelayoutParams, startT, mResult);
+        }
+    }
+
+    /**
+     * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been
+     * updated.
+     */
+    private void clearCurrentViewHostRunnable() {
+        if (mCurrentViewHostRunnable != null) {
+            mHandler.removeCallbacks(mCurrentViewHostRunnable);
+            mCurrentViewHostRunnable = null;
+        }
+    }
+
+    /**
+     * Relayout the window decoration but repost some of the work, to unblock the current callstack.
+     */
+    private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+        if (applyStartTransactionOnDraw) {
+            throw new IllegalArgumentException(
+                    "We cannot both sync viewhost ondraw and delay viewhost creation.");
+        }
+        // Clear the current ViewHost runnable as we will update the ViewHost here
+        clearCurrentViewHostRunnable();
+        updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
+                false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop);
+        if (mResult.mRootView == null) {
+            // This means something blocks the window decor from showing, e.g. the task is hidden.
+            // Nothing is set up in this case including the decoration surface.
+            return;
+        }
+        // Store the current runnable so it can be removed if we start a new relayout.
+        mCurrentViewHostRunnable = mViewHostRunnable;
+        mHandler.post(mCurrentViewHostRunnable);
+    }
+
+    @SuppressLint("MissingPermission")
+    private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+        Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
 
         if (Flags.enableDesktopWindowingAppToWeb()) {
             setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
@@ -384,8 +446,8 @@
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
-        Trace.beginSection("DesktopModeWindowDecoration#relayout-inner");
-        relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
+        Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces");
+        updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
         Trace.endSection();
         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
 
@@ -400,7 +462,7 @@
                 notifyNoCaptionHandle();
             }
             disposeStatusBarInputLayer();
-            Trace.endSection(); // DesktopModeWindowDecoration#relayout
+            Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
             return;
         }
 
@@ -408,12 +470,12 @@
             disposeStatusBarInputLayer();
             mWindowDecorViewHolder = createViewHolder();
         }
+        Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
 
         final Point position = new Point();
         if (isAppHandle(mWindowDecorViewHolder)) {
             position.set(determineHandlePosition());
         }
-        Trace.beginSection("DesktopModeWindowDecoration#relayout-bindData");
         if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
             notifyCaptionStateChanged();
         }
@@ -431,7 +493,7 @@
         }
         updateDragResizeListener(oldDecorationSurface);
         updateMaximizeMenu(startT);
-        Trace.endSection(); // DesktopModeWindowDecoration#relayout
+        Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
     }
 
     private boolean isCaptionVisible() {
@@ -477,7 +539,7 @@
     }
 
     private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
-        if (!isDragResizable(mTaskInfo, mContext)) {
+        if (!isDragResizable(mTaskInfo)) {
             if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
                 // We still want to track caption bar's exclusion region on a non-resizeable task.
                 updateExclusionRegion();
@@ -504,7 +566,6 @@
 
         final int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext())
                 .getScaledTouchSlop();
-        mDragDetector.setTouchSlop(touchSlop);
 
         // If either task geometry or position have changed, update this task's
         // exclusion region listener
@@ -512,16 +573,15 @@
         if (mDragResizeListener.setGeometry(
                 new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
                         new Size(mResult.mWidth, mResult.mHeight),
-                        getResizeEdgeHandleSize(mContext, res), getResizeHandleEdgeInset(res),
+                        getResizeEdgeHandleSize(res), getResizeHandleEdgeInset(res),
                         getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop)
                 || !mTaskInfo.positionInParent.equals(mPositionInParent)) {
             updateExclusionRegion();
         }
     }
 
-    private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo,
-            Context context) {
-        if (DesktopModeFlags.SCALED_RESIZING.isEnabled(context)) {
+    private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
+        if (DesktopModeFlags.ENABLE_WINDOWING_SCALED_RESIZING.isTrue()) {
             return taskInfo.isFreeform();
         }
         return taskInfo.isFreeform() && taskInfo.isResizeable;
@@ -584,12 +644,8 @@
         mWindowDecorCaptionHandleRepository.notifyCaptionChanged(captionState);
     }
 
-    private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
-        return taskInfo.isFreeform() && taskInfo.isResizeable;
-    }
-
     private void updateMaximizeMenu(SurfaceControl.Transaction startT) {
-        if (!isDragResizable(mTaskInfo, mContext) || !isMaximizeMenuActive()) {
+        if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) {
             return;
         }
         if (!mTaskInfo.isVisible()) {
@@ -622,7 +678,7 @@
                 || !Flags.enableHandleInputFix()) {
             return;
         }
-        ((AppHandleViewHolder) mWindowDecorViewHolder).disposeStatusBarInputLayer();
+        asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
     }
 
     private WindowDecorationViewHolder createViewHolder() {
@@ -659,6 +715,22 @@
         return viewHolder instanceof AppHandleViewHolder;
     }
 
+    @Nullable
+    private AppHandleViewHolder asAppHandle(WindowDecorationViewHolder viewHolder) {
+        if (viewHolder instanceof AppHandleViewHolder) {
+            return (AppHandleViewHolder) viewHolder;
+        }
+        return null;
+    }
+
+    @Nullable
+    private AppHeaderViewHolder asAppHeader(WindowDecorationViewHolder viewHolder) {
+        if (viewHolder instanceof AppHeaderViewHolder) {
+            return (AppHeaderViewHolder) viewHolder;
+        }
+        return null;
+    }
+
     @VisibleForTesting
     static void updateRelayoutParams(
             RelayoutParams relayoutParams,
@@ -675,10 +747,6 @@
         relayoutParams.mLayoutResId = captionLayoutId;
         relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
         relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
-        // Allow the handle view to be delayed since the handle is just a small addition to the
-        // window, whereas the header cannot be delayed because it is expected to be visible from
-        // the first frame.
-        relayoutParams.mAsyncViewHost = isAppHandle;
 
         if (isAppHeader) {
             if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
@@ -687,13 +755,13 @@
                 // their custom content.
                 relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
             } else {
-                if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
+                if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()) {
                     // Force-consume the caption bar insets when the app tries to hide the caption.
                     // This improves app compatibility of immersive apps.
                     relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING;
                 }
             }
-            if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isEnabled()) {
+            if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue()) {
                 // Always force-consume the caption bar insets for maximum app compatibility,
                 // including non-immersive apps that just don't handle caption insets properly.
                 relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
@@ -739,7 +807,7 @@
         // TODO(b/301119301): consider moving the config data needed for diffs to relayout params
         // instead of using a whole Configuration as a parameter.
         final Configuration windowDecorConfig = new Configuration();
-        if (DesktopModeFlags.APP_HEADER_WITH_TASK_DENSITY.isEnabled(context) && isAppHeader) {
+        if (DesktopModeFlags.ENABLE_APP_HEADER_WITH_TASK_DENSITY.isTrue() && isAppHeader) {
             // Should match the density of the task. The task may have had its density overridden
             // to be different that SysUI's.
             windowDecorConfig.setTo(taskInfo.configuration);
@@ -1037,7 +1105,15 @@
      */
     void closeMaximizeMenu() {
         if (!isMaximizeMenuActive()) return;
-        mMaximizeMenu.close();
+        mMaximizeMenu.close(() -> {
+            // Request the accessibility service to refocus on the maximize button after closing
+            // the menu.
+            final AppHeaderViewHolder appHeader = asAppHeader(mWindowDecorViewHolder);
+            if (appHeader != null) {
+                appHeader.requestAccessibilityFocus();
+            }
+            return Unit.INSTANCE;
+        });
         mMaximizeMenu = null;
     }
 
@@ -1065,7 +1141,8 @@
         loadAppInfoIfNeeded();
         updateGenericLink();
         final boolean supportsMultiInstance = mMultiInstanceHelper
-                .supportsMultiInstanceSplit(mTaskInfo.baseActivity);
+                .supportsMultiInstanceSplit(mTaskInfo.baseActivity)
+                && Flags.enableDesktopWindowingMultiInstanceFeatures();
         final boolean shouldShowManageWindowsButton = supportsMultiInstance
                 && mMinimumInstancesFound;
         mHandleMenu = mHandleMenuFactory.create(
@@ -1352,10 +1429,10 @@
         mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
         disposeResizeVeil();
         disposeStatusBarInputLayer();
+        clearCurrentViewHostRunnable();
         if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
             notifyNoCaptionHandle();
         }
-
         super.close();
     }
 
@@ -1382,7 +1459,7 @@
      */
     private Region getGlobalExclusionRegion() {
         Region exclusionRegion;
-        if (mDragResizeListener != null && mTaskInfo.isResizeable) {
+        if (mDragResizeListener != null && isDragResizable(mTaskInfo)) {
             exclusionRegion = mDragResizeListener.getCornersRegion();
         } else {
             exclusionRegion = new Region();
@@ -1419,7 +1496,7 @@
 
     void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) {
         if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return;
-        ((AppHeaderViewHolder) mWindowDecorViewHolder)
+        asAppHeader(mWindowDecorViewHolder)
                 .setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition);
     }
 
@@ -1427,16 +1504,14 @@
      * Called when there is a {@link MotionEvent#ACTION_HOVER_EXIT} on the maximize window button.
      */
     void onMaximizeButtonHoverExit() {
-        ((AppHeaderViewHolder) mWindowDecorViewHolder)
-                .onMaximizeWindowHoverExit();
+        asAppHeader(mWindowDecorViewHolder).onMaximizeWindowHoverExit();
     }
 
     /**
      * Called when there is a {@link MotionEvent#ACTION_HOVER_ENTER} on the maximize window button.
      */
     void onMaximizeButtonHoverEnter() {
-        ((AppHeaderViewHolder) mWindowDecorViewHolder)
-                .onMaximizeWindowHoverEnter();
+        asAppHeader(mWindowDecorViewHolder).onMaximizeWindowHoverEnter();
     }
 
     @Override
@@ -1468,8 +1543,7 @@
                 AppToWebGenericLinksParser genericLinksParser,
                 AssistContentRequester assistContentRequester,
                 MultiInstanceHelper multiInstanceHelper,
-                WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
-                WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+                WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) {
             return new DesktopModeWindowDecoration(
                     context,
                     userContext,
@@ -1487,8 +1561,7 @@
                     genericLinksParser,
                     assistContentRequester,
                     multiInstanceHelper,
-                    windowDecorCaptionHandleRepository,
-                    windowDecorViewHostSupplier);
+                    windowDecorCaptionHandleRepository);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 3fd3656..01bb7f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -26,6 +26,7 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
+import android.annotation.NonNull;
 import android.graphics.PointF;
 import android.view.MotionEvent;
 import android.view.View;
@@ -48,12 +49,18 @@
     private int mTouchSlop;
     private boolean mIsDragEvent;
     private int mDragPointerId = -1;
+    private final long mHoldToDragMinDurationMs;
+    private boolean mDidStrayBeforeFullHold;
+    private boolean mDidHoldForMinDuration;
 
     private boolean mResultOfDownAction;
 
-    DragDetector(MotionEventHandler eventHandler) {
+    DragDetector(@NonNull MotionEventHandler eventHandler, long holdToDragMinDurationMs,
+            int touchSlop) {
         resetState();
         mEventHandler = eventHandler;
+        mHoldToDragMinDurationMs = holdToDragMinDurationMs;
+        mTouchSlop = touchSlop;
     }
 
     /**
@@ -101,9 +108,26 @@
                 if (!mIsDragEvent) {
                     float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
                     float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
+                    final float dt = ev.getEventTime() - ev.getDownTime();
+                    final boolean pastTouchSlop = Math.hypot(dx, dy) > mTouchSlop;
+                    final boolean withinHoldRegion = !pastTouchSlop;
+
+                    if (mHoldToDragMinDurationMs <= 0) {
+                        mDidHoldForMinDuration = true;
+                    } else {
+                        if (!withinHoldRegion && dt < mHoldToDragMinDurationMs) {
+                            // Mark as having strayed so that in case the (x,y) ends up in the
+                            // original position we know it's not actually valid.
+                            mDidStrayBeforeFullHold = true;
+                        }
+                        if (!mDidStrayBeforeFullHold && dt >= mHoldToDragMinDurationMs) {
+                            mDidHoldForMinDuration = true;
+                        }
+                    }
+
                     // Touches generate noisy moves, so only once the move is past the touch
                     // slop threshold should it be considered a drag.
-                    mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
+                    mIsDragEvent = mDidHoldForMinDuration && pastTouchSlop;
                 }
                 // The event handler should only be notified about 'move' events if a drag has been
                 // detected.
@@ -162,6 +186,8 @@
         mInputDownPoint.set(0, 0);
         mDragPointerId = -1;
         mResultOfDownAction = false;
+        mDidStrayBeforeFullHold = false;
+        mDidHoldForMinDuration = false;
     }
 
     interface MotionEventHandler {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index cad3462..38f9cfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -27,12 +27,12 @@
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
+import android.window.flags.DesktopModeFlags;
 
 import androidx.annotation.NonNull;
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
 /**
@@ -145,7 +145,7 @@
         // If the application is unresizeable and any bounds have been set back to their old
         // location or to a stable bound edge, reset all the bounds to maintain the applications
         // aspect ratio.
-        if (DesktopModeFlags.SCALED_RESIZING.isEnabled(windowDecoration.mDecorWindowContext)
+        if (DesktopModeFlags.ENABLE_WINDOWING_SCALED_RESIZING.isTrue()
                 && !isAspectRatioMaintained && !windowDecoration.mTaskInfo.isResizeable) {
             repositionTaskBounds.top = oldTop;
             repositionTaskBounds.bottom = oldBottom;
@@ -275,7 +275,7 @@
 
     private static boolean isSizeConstraintForDesktopModeEnabled(Context context) {
         return DesktopModeStatus.canEnterDesktopMode(context)
-                && DesktopModeFlags.SIZE_CONSTRAINTS.isEnabled(context);
+                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS.isTrue();
     }
 
     interface DragStartListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index a27c506..4ff394e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -183,7 +183,7 @@
 
         mTouchRegion.setEmpty();
         // Apply the geometry to the touch region.
-        geometry.union(mContext, mTouchRegion);
+        geometry.union(mTouchRegion);
         mInputEventReceiver.setGeometry(geometry);
         mInputEventReceiver.setTouchRegion(mTouchRegion);
 
@@ -318,7 +318,8 @@
                 }
             };
 
-            mDragDetector = new DragDetector(this);
+            mDragDetector = new DragDetector(this, 0 /* holdToDragMinDurationMs */,
+                    ViewConfiguration.get(mContext).getScaledTouchSlop());
             mDisplayLayoutSizeSupplier = displayLayoutSizeSupplier;
             mTouchRegionConsumer = touchRegionConsumer;
         }
@@ -357,7 +358,7 @@
          */
         @NonNull Region getCornersRegion() {
             Region region = new Region();
-            mDragResizeWindowGeometry.union(mContext, region);
+            mDragResizeWindowGeometry.union(region);
             return region;
         }
 
@@ -408,8 +409,8 @@
                         float y = e.getY(0);
                         float rawX = e.getRawX(0);
                         float rawY = e.getRawY(0);
-                        final int ctrlType = mDragResizeWindowGeometry.calculateCtrlType(mContext,
-                                isEventFromTouchscreen(e), isEdgeResizePermitted(mContext, e), x,
+                        final int ctrlType = mDragResizeWindowGeometry.calculateCtrlType(
+                                isEventFromTouchscreen(e), isEdgeResizePermitted(e), x,
                                 y);
                         ProtoLog.d(WM_SHELL_DESKTOP_MODE,
                                 "%s: Handling action down, update ctrlType to %d", TAG, ctrlType);
@@ -500,8 +501,8 @@
             // Since we are handling cursor, we know that this is not a touchscreen event, and
             // that edge resizing should always be allowed.
             @DragPositioningCallback.CtrlType int ctrlType =
-                    mDragResizeWindowGeometry.calculateCtrlType(mContext, /* isTouchscreen= */
-                            false, /* isEdgeResizePermitted= */ true, x, y);
+                    mDragResizeWindowGeometry.calculateCtrlType(/* isTouchscreen= */ false,
+                            /* isEdgeResizePermitted= */ true, x, y);
 
             int cursorType = PointerIcon.TYPE_DEFAULT;
             switch (ctrlType) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index 6dedf6d..d726f50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -18,8 +18,8 @@
 
 import static android.view.InputDevice.SOURCE_MOUSE;
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.window.flags.DesktopModeFlags.ENABLE_WINDOWING_EDGE_DRAG_RESIZE;
 
-import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.EDGE_DRAG_RESIZE;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
@@ -76,8 +76,8 @@
     /**
      * Returns the resource value to use for the resize handle on the edge of the window.
      */
-    static int getResizeEdgeHandleSize(@NonNull Context context, @NonNull Resources res) {
-        return EDGE_DRAG_RESIZE.isEnabled(context)
+    static int getResizeEdgeHandleSize(@NonNull Resources res) {
+        return ENABLE_WINDOWING_EDGE_DRAG_RESIZE.isTrue()
                 ? res.getDimensionPixelSize(R.dimen.freeform_edge_handle_outset)
                 : res.getDimensionPixelSize(R.dimen.freeform_resize_handle);
     }
@@ -118,11 +118,11 @@
      * Returns the union of all regions that can be touched for drag resizing; the corners window
      * and window edges.
      */
-    void union(@NonNull Context context, @NonNull Region region) {
+    void union(@NonNull Region region) {
         // Apply the edge resize regions.
         mTaskEdges.union(region);
 
-        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
+        if (ENABLE_WINDOWING_EDGE_DRAG_RESIZE.isTrue()) {
             // Apply the corners as well for the larger corners, to ensure we capture all possible
             // touches.
             mLargeTaskCorners.union(region);
@@ -140,7 +140,7 @@
         final float x = e.getX(0) + offset.x;
         final float y = e.getY(0) + offset.y;
 
-        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
+        if (ENABLE_WINDOWING_EDGE_DRAG_RESIZE.isTrue()) {
             // First check if touch falls within a corner.
             // Large corner bounds are used for course input like touch, otherwise fine bounds.
             boolean result = isEventFromTouchscreen(e)
@@ -148,7 +148,7 @@
                     : isInCornerBounds(mFineTaskCorners, x, y);
             // Check if touch falls within the edge resize handle. Limit edge resizing to stylus and
             // mouse input.
-            if (!result && isEdgeResizePermitted(context, e)) {
+            if (!result && isEdgeResizePermitted(e)) {
                 result = isInEdgeResizeBounds(x, y);
             }
             return result;
@@ -164,8 +164,8 @@
         return (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
     }
 
-    static boolean isEdgeResizePermitted(@NonNull Context context, @NonNull MotionEvent e) {
-        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
+    static boolean isEdgeResizePermitted(@NonNull MotionEvent e) {
+        if (ENABLE_WINDOWING_EDGE_DRAG_RESIZE.isTrue()) {
             return e.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
                     || e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE
                     // Touchpad input
@@ -193,9 +193,8 @@
      *                              resize region.
      */
     @DragPositioningCallback.CtrlType
-    int calculateCtrlType(@NonNull Context context, boolean isTouchscreen,
-            boolean isEdgeResizePermitted, float x, float y) {
-        if (EDGE_DRAG_RESIZE.isEnabled(context)) {
+    int calculateCtrlType(boolean isTouchscreen, boolean isEdgeResizePermitted, float x, float y) {
+        if (ENABLE_WINDOWING_EDGE_DRAG_RESIZE.isTrue()) {
             // First check if touch falls within a corner.
             // Large corner bounds are used for course input like touch, otherwise fine bounds.
             int ctrlType = isTouchscreen
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 faffe4a..9a5b4f5 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
@@ -23,8 +23,6 @@
 import android.content.res.ColorStateList
 import android.content.res.Resources
 import android.graphics.Bitmap
-import android.graphics.BlendMode
-import android.graphics.BlendModeColorFilter
 import android.graphics.Point
 import android.graphics.PointF
 import android.graphics.Rect
@@ -568,9 +566,7 @@
             appIconBitmap: Bitmap?,
             appName: CharSequence?
         ) {
-            appInfoPill.background.colorFilter = BlendModeColorFilter(
-                style.backgroundColor, BlendMode.MULTIPLY
-            )
+            appInfoPill.background.setTint(style.backgroundColor)
 
             collapseMenuButton.apply {
                 imageTintList = ColorStateList.valueOf(style.textColor)
@@ -584,20 +580,22 @@
         }
 
         private fun bindWindowingPill(style: MenuStyle) {
-            windowingPill.background.colorFilter = BlendModeColorFilter(
-                style.backgroundColor, BlendMode.MULTIPLY
-            )
+            windowingPill.background.setTint(style.backgroundColor)
 
             // TODO: Remove once implemented.
             floatingBtn.visibility = View.GONE
 
             fullscreenBtn.isSelected = taskInfo.isFullscreen
+            fullscreenBtn.isEnabled = !taskInfo.isFullscreen
             fullscreenBtn.imageTintList = style.windowingButtonColor
             splitscreenBtn.isSelected = taskInfo.isMultiWindow
+            splitscreenBtn.isEnabled = !taskInfo.isMultiWindow
             splitscreenBtn.imageTintList = style.windowingButtonColor
             floatingBtn.isSelected = taskInfo.isPinned
+            floatingBtn.isEnabled = !taskInfo.isPinned
             floatingBtn.imageTintList = style.windowingButtonColor
             desktopBtn.isSelected = taskInfo.isFreeform
+            desktopBtn.isEnabled = !taskInfo.isFreeform
             desktopBtn.imageTintList = style.windowingButtonColor
         }
 
@@ -608,23 +606,19 @@
             }
             screenshotBtn.apply {
                 isGone = !SHOULD_SHOW_SCREENSHOT_BUTTON
-                background.colorFilter =
-                    BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY
-                )
+                background.setTint(style.backgroundColor)
                 setTextColor(style.textColor)
                 compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
             }
             newWindowBtn.apply {
                 isGone = !shouldShowNewWindowButton
-                background.colorFilter =
-                    BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+                background.setTint(style.backgroundColor)
                 setTextColor(style.textColor)
                 compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
             }
             manageWindowBtn.apply {
                 isGone = !shouldShowManageWindowsButton
-                background.colorFilter =
-                    BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+                background.setTint(style.backgroundColor)
                 setTextColor(style.textColor)
                 compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
             }
@@ -633,9 +627,7 @@
         private fun bindOpenInBrowserPill(style: MenuStyle) {
             openInBrowserPill.apply {
                 isGone = !shouldShowBrowserPill
-                background.colorFilter = BlendModeColorFilter(
-                    style.backgroundColor, BlendMode.MULTIPLY
-                )
+                background.setTint(style.backgroundColor)
             }
 
             browserBtn.apply {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
index 9590ccd..0c475f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
@@ -26,6 +26,7 @@
 import android.view.View.TRANSLATION_Y
 import android.view.View.TRANSLATION_Z
 import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
 import android.widget.Button
 import androidx.core.animation.doOnEnd
 import androidx.core.view.children
@@ -83,7 +84,12 @@
         animateWindowingPillOpen()
         animateMoreActionsPillOpen()
         animateOpenInBrowserPill()
-        runAnimations()
+        runAnimations {
+            appInfoPill.post {
+                appInfoPill.requireViewById<View>(R.id.collapse_menu_button).sendAccessibilityEvent(
+                    AccessibilityEvent.TYPE_VIEW_FOCUSED)
+            }
+        }
     }
 
     /**
@@ -98,7 +104,12 @@
         animateWindowingPillOpen()
         animateMoreActionsPillOpen()
         animateOpenInBrowserPill()
-        runAnimations()
+        runAnimations {
+            appInfoPill.post {
+                appInfoPill.requireViewById<View>(R.id.collapse_menu_button).sendAccessibilityEvent(
+                    AccessibilityEvent.TYPE_VIEW_FOCUSED)
+            }
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 4faed01..2d97dc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -32,7 +32,7 @@
 import androidx.core.animation.doOnStart
 import androidx.core.content.ContextCompat
 import com.android.wm.shell.R
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
+import android.window.flags.DesktopModeFlags
 
 private const val OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS = 350
 private const val MAX_DRAWABLE_ALPHA = 255
@@ -108,7 +108,7 @@
         baseForegroundColor: Int? = null,
         rippleDrawable: RippleDrawable? = null
     ) {
-        if (DesktopModeFlags.THEMED_APP_HEADERS.isEnabled(context)) {
+        if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
             requireNotNull(iconForegroundColor) { "Icon foreground color must be non-null" }
             requireNotNull(baseForegroundColor) { "Base foreground color must be non-null" }
             requireNotNull(rippleDrawable) { "Ripple drawable must be non-null" }
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 9c73e4a..0cb219a 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
@@ -51,6 +51,7 @@
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.WindowlessWindowManager
+import android.view.accessibility.AccessibilityEvent
 import android.widget.Button
 import android.widget.TextView
 import android.window.TaskConstants
@@ -116,19 +117,24 @@
             onHoverListener = onHoverListener,
             onOutsideTouchListener = onOutsideTouchListener
         )
-        maximizeMenuView?.animateOpenMenu()
+        maximizeMenuView?.let { view ->
+            view.animateOpenMenu(onEnd = {
+                view.requestAccessibilityFocus()
+            })
+        }
     }
 
     /** Closes the maximize window and releases its view. */
-    fun close() {
+    fun close(onEnd: () -> Unit) {
         val view = maximizeMenuView
         val menu = maximizeMenu
         if (view == null) {
             menu?.releaseView()
         } else {
-            view.animateCloseMenu {
+            view.animateCloseMenu(onEnd = {
                 menu?.releaseView()
-            }
+                onEnd.invoke()
+            })
         }
         maximizeMenu = null
         maximizeMenuView = null
@@ -351,7 +357,7 @@
         }
 
         /** Animate the opening of the menu */
-        fun animateOpenMenu() {
+        fun animateOpenMenu(onEnd: () -> Unit) {
             maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
             menuAnimatorSet = AnimatorSet()
@@ -419,6 +425,7 @@
                 onEnd = {
                     maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
                     maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                    onEnd.invoke()
                 }
             )
             menuAnimatorSet?.start()
@@ -499,6 +506,14 @@
             menuAnimatorSet?.start()
         }
 
+        /** Request that the accessibility service focus on the menu. */
+        fun requestAccessibilityFocus() {
+            // Focus the first button in the menu by default.
+            maximizeButton.post {
+                maximizeButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+            }
+        }
+
         /** Cancel the menu animation. */
         private fun cancelAnimation() {
             menuAnimatorSet?.cancel()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 3694845..c1a55b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -62,8 +62,6 @@
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer;
 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -118,7 +116,6 @@
     final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
     final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
     final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
-    @NonNull private final WindowDecorViewHostSupplier mWindowDecorViewHostSupplier;
     private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
             new DisplayController.OnDisplaysChangedListener() {
                 @Override
@@ -140,7 +137,9 @@
     Context mDecorWindowContext;
     SurfaceControl mDecorationContainerSurface;
 
-    private WindowDecorViewHost mDecorViewHost;
+    SurfaceControl mCaptionContainerSurface;
+    private WindowlessWindowManager mCaptionWindowManager;
+    private SurfaceControlViewHost mViewHost;
     private Configuration mWindowDecorConfig;
     TaskDragResizer mTaskDragResizer;
     boolean mIsCaptionVisible;
@@ -159,13 +158,11 @@
             DisplayController displayController,
             ShellTaskOrganizer taskOrganizer,
             RunningTaskInfo taskInfo,
-            SurfaceControl taskSurface,
-            @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            SurfaceControl taskSurface) {
         this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                 SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
                 WindowContainerTransaction::new, SurfaceControl::new,
-                new SurfaceControlViewHostFactory() {},
-                windowDecorViewHostSupplier);
+                new SurfaceControlViewHostFactory() {});
     }
 
     WindowDecoration(
@@ -179,8 +176,7 @@
             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
             Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
             Supplier<SurfaceControl> surfaceControlSupplier,
-            SurfaceControlViewHostFactory surfaceControlViewHostFactory,
-            @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+            SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
         mContext = context;
         mUserContext = userContext;
         mDisplayController = displayController;
@@ -191,7 +187,6 @@
         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
         mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
-        mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
         final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId);
         mIsStatusBarVisible = insetsState != null
@@ -217,7 +212,15 @@
     void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
             RelayoutResult<T> outResult) {
-        Trace.beginSection("WindowDecoration#relayout");
+        updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult);
+        if (outResult.mRootView != null) {
+            updateViewHost(params, startT, outResult);
+        }
+    }
+
+    protected void updateViewsAndSurfaces(RelayoutParams params,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) {
         outResult.reset();
         if (params.mRunningTaskInfo != null) {
             mTaskInfo = params.mRunningTaskInfo;
@@ -228,21 +231,17 @@
         if (!mTaskInfo.isVisible) {
             releaseViews(wct);
             finishT.hide(mTaskSurface);
-            Trace.endSection(); // WindowDecoration#relayout
-            return;
-        }
-        Trace.beginSection("WindowDecoration#relayout-inflateIfNeeded");
-        inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
-        Trace.endSection();
-        final boolean hasCaptionView = outResult.mRootView != null;
-        if (!hasCaptionView) {
-            Trace.endSection(); // WindowDecoration#relayout
             return;
         }
 
-        Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility");
+        inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
+        if (outResult.mRootView == null) {
+            // Didn't manage to create a root view, early out.
+            return;
+        }
+        rootView = null; // Clear it just in case we use it accidentally
+
         updateCaptionVisibility(outResult.mRootView);
-        Trace.endSection();
 
         final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
         outResult.mWidth = taskBounds.width();
@@ -255,23 +254,10 @@
                 ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
         outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
 
-        Trace.beginSection("WindowDecoration#relayout-acquire");
-        if (mDecorViewHost == null) {
-            mDecorViewHost = mWindowDecorViewHostSupplier.acquire(mDecorWindowContext, mDisplay);
-        }
-        Trace.endSection();
-
-        final SurfaceControl captionSurface = mDecorViewHost.getSurfaceControl();
-        Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets");
         updateDecorationContainerSurface(startT, outResult);
-        updateCaptionContainerSurface(captionSurface, startT, outResult);
+        updateCaptionContainerSurface(startT, outResult);
         updateCaptionInsets(params, wct, outResult, taskBounds);
         updateTaskSurface(params, startT, finishT, outResult);
-        Trace.endSection();
-
-        outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
-        updateViewHierarchy(params, outResult, startT);
-        Trace.endSection(); // WindowDecoration#relayout
     }
 
     private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
@@ -319,32 +305,6 @@
         return (T) LayoutInflater.from(context).inflate(layoutResId, null);
     }
 
-    private void updateViewHierarchy(@NonNull RelayoutParams params,
-            @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT) {
-        Trace.beginSection("WindowDecoration#updateViewHierarchy");
-        final WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(
-                        outResult.mCaptionWidth,
-                        outResult.mCaptionHeight,
-                        TYPE_APPLICATION,
-                        FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
-                        PixelFormat.TRANSPARENT);
-        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
-        lp.setTrustedOverlay();
-        lp.inputFeatures = params.mInputFeatures;
-        if (params.mAsyncViewHost) {
-            if (params.mApplyStartTransactionOnDraw) {
-                throw new IllegalArgumentException(
-                        "We cannot both sync viewhost ondraw and delay viewhost creation.");
-            }
-            mDecorViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.getConfiguration());
-        } else {
-            mDecorViewHost.updateView(outResult.mRootView, lp, mTaskInfo.getConfiguration(),
-                    params.mApplyStartTransactionOnDraw ? startT : null);
-        }
-        Trace.endSection();
-    }
-
     private void updateDecorationContainerSurface(
             SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
         if (mDecorationContainerSurface == null) {
@@ -365,14 +325,23 @@
                 .show(mDecorationContainerSurface);
     }
 
-    private void updateCaptionContainerSurface(@NonNull SurfaceControl captionSurface,
+    private void updateCaptionContainerSurface(
             SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
-        startT.reparent(captionSurface, mDecorationContainerSurface)
-                .setWindowCrop(captionSurface, outResult.mCaptionWidth,
+        if (mCaptionContainerSurface == null) {
+            final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
+            mCaptionContainerSurface = builder
+                    .setName("Caption container of Task=" + mTaskInfo.taskId)
+                    .setContainerLayer()
+                    .setParent(mDecorationContainerSurface)
+                    .setCallsite("WindowDecoration.updateCaptionContainerSurface")
+                    .build();
+        }
+
+        startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
                         outResult.mCaptionHeight)
-                .setPosition(captionSurface, outResult.mCaptionX, 0 /* y */)
-                .setLayer(captionSurface, CAPTION_LAYER_Z_ORDER)
-                .show(captionSurface);
+                .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
+                .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
+                .show(mCaptionContainerSurface);
     }
 
     private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
@@ -466,6 +435,64 @@
         }
     }
 
+    /**
+     * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our
+     * View hierarchy.
+     *
+     * @param params parameters to use from the last relayout
+     * @param onDrawTransaction a transaction to apply in sync with #onDraw
+     * @param outResult results to use from the last relayout
+     *
+     */
+    protected void updateViewHost(RelayoutParams params,
+            SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) {
+        Trace.beginSection("CaptionViewHostLayout");
+        if (mCaptionWindowManager == null) {
+            // Put caption under a container surface because ViewRootImpl sets the destination frame
+            // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
+            mCaptionWindowManager = new WindowlessWindowManager(
+                    mTaskInfo.getConfiguration(), mCaptionContainerSurface,
+                    null /* hostInputToken */);
+        }
+        mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
+        final WindowManager.LayoutParams lp =
+                new WindowManager.LayoutParams(
+                        outResult.mCaptionWidth,
+                        outResult.mCaptionHeight,
+                        TYPE_APPLICATION,
+                        FLAG_NOT_FOCUSABLE | FLAG_SPLIT_TOUCH,
+                        PixelFormat.TRANSPARENT);
+        lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
+        lp.setTrustedOverlay();
+        lp.inputFeatures = params.mInputFeatures;
+        if (mViewHost == null) {
+            Trace.beginSection("CaptionViewHostLayout-new");
+            mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
+                    mCaptionWindowManager);
+            if (params.mApplyStartTransactionOnDraw) {
+                if (onDrawTransaction == null) {
+                    throw new IllegalArgumentException("Trying to sync a null Transaction");
+                }
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
+            }
+            outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+            mViewHost.setView(outResult.mRootView, lp);
+            Trace.endSection();
+        } else {
+            Trace.beginSection("CaptionViewHostLayout-relayout");
+            if (params.mApplyStartTransactionOnDraw) {
+                if (onDrawTransaction == null) {
+                    throw new IllegalArgumentException("Trying to sync a null Transaction");
+                }
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
+            }
+            outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0);
+            mViewHost.relayout(lp);
+            Trace.endSection();
+        }
+        Trace.endSection(); // CaptionViewHostLayout
+    }
+
     private Rect calculateBoundingRect(@NonNull OccludingCaptionElement element,
             int elementWidthPx, @NonNull Rect captionRect) {
         switch (element.mAlignment) {
@@ -553,11 +580,18 @@
     }
 
     void releaseViews(WindowContainerTransaction wct) {
+        if (mViewHost != null) {
+            mViewHost.release();
+            mViewHost = null;
+        }
+
+        mCaptionWindowManager = null;
+
         final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
         boolean released = false;
-        if (mDecorViewHost != null) {
-            mWindowDecorViewHostSupplier.release(mDecorViewHost, t);
-            mDecorViewHost = null;
+        if (mCaptionContainerSurface != null) {
+            t.remove(mCaptionContainerSurface);
+            mCaptionContainerSurface = null;
             released = true;
         }
 
@@ -708,7 +742,6 @@
 
         boolean mApplyStartTransactionOnDraw;
         boolean mSetTaskPositionAndCrop;
-        boolean mAsyncViewHost;
 
         void reset() {
             mLayoutResId = Resources.ID_NULL;
@@ -725,7 +758,6 @@
 
             mApplyStartTransactionOnDraw = false;
             mSetTaskPositionAndCrop = false;
-            mAsyncViewHost = false;
             mWindowDecorConfig = null;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 226b0fb..1be26f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -107,4 +107,27 @@
         }
         windowManagerWrapper.updateViewLayout(view, lp)
     }
+
+    class Factory {
+        fun create(
+            windowManagerWrapper: WindowManagerWrapper,
+            taskId: Int,
+            x: Int,
+            y: Int,
+            width: Int,
+            height: Int,
+            flags: Int,
+            view: View,
+        ): AdditionalSystemViewContainer =
+            AdditionalSystemViewContainer(
+                windowManagerWrapper = windowManagerWrapper,
+                taskId = taskId,
+                x = x,
+                y = y,
+                width = width,
+                height = height,
+                flags = flags,
+                view = view
+            )
+    }
 }
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
new file mode 100644
index 0000000..98413ee
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
@@ -0,0 +1,249 @@
+/*
+ * 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.windowdecor.education
+
+import android.annotation.DimenRes
+import android.annotation.LayoutRes
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Point
+import android.util.Size
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.View.MeasureSpec.UNSPECIFIED
+import android.view.WindowManager
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.wm.shell.R
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+
+/**
+ * Controls the lifecycle of an education tooltip, including showing and hiding it. Ensures that
+ * only one tooltip is displayed at a time.
+ */
+class DesktopWindowingEducationTooltipController(
+    private val context: Context,
+    private val additionalSystemViewContainerFactory: AdditionalSystemViewContainer.Factory,
+) {
+  // 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 {
+    PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+  }
+  private var popupWindow: AdditionalSystemViewContainer? = null
+
+  /**
+   * Shows education tooltip.
+   *
+   * @param tooltipViewConfig features of tooltip.
+   * @param taskId is used in the title of popup window created for the tooltip view.
+   */
+  fun showEducationTooltip(tooltipViewConfig: EducationViewConfig, taskId: Int) {
+    hideEducationTooltip()
+    tooltipView = createEducationTooltipView(tooltipViewConfig, taskId)
+    animator = createAnimator()
+    animateShowTooltipTransition()
+  }
+
+  /** Hide the current education view if visible */
+  private fun hideEducationTooltip() = animateHideTooltipTransition { cleanUp() }
+
+  /** Create education view by inflating layout provided. */
+  private fun createEducationTooltipView(
+      tooltipViewConfig: EducationViewConfig,
+      taskId: Int,
+  ): View {
+    val tooltipView =
+        LayoutInflater.from(context)
+            .inflate(
+                tooltipViewConfig.tooltipViewLayout, /* root= */ null, /* attachToRoot= */ false)
+            .apply {
+              alpha = 0f
+              scaleX = 0f
+              scaleY = 0f
+
+              requireViewById<TextView>(R.id.tooltip_text).apply {
+                text = tooltipViewConfig.tooltipText
+              }
+
+              setOnTouchListener { _, motionEvent ->
+                if (motionEvent.action == MotionEvent.ACTION_OUTSIDE) {
+                  hideEducationTooltip()
+                  tooltipViewConfig.onDismissAction()
+                  true
+                } else {
+                  false
+                }
+              }
+              setOnClickListener {
+                hideEducationTooltip()
+                tooltipViewConfig.onEducationClickAction()
+              }
+            }
+
+    val tooltipDimens = tooltipDimens(tooltipView = tooltipView, tooltipViewConfig.arrowDirection)
+    val tooltipViewGlobalCoordinates =
+        tooltipViewGlobalCoordinates(
+            tooltipViewGlobalCoordinates = tooltipViewConfig.tooltipViewGlobalCoordinates,
+            arrowDirection = tooltipViewConfig.arrowDirection,
+            tooltipDimen = tooltipDimens)
+    createTooltipPopupWindow(
+        taskId, tooltipViewGlobalCoordinates, tooltipDimens, tooltipView = tooltipView)
+
+    return tooltipView
+  }
+
+  /** Create animator for education transitions */
+  private fun createAnimator(): PhysicsAnimator<View>? =
+      tooltipView?.let {
+        PhysicsAnimator.getInstance(it).apply { setDefaultSpringConfig(springConfig) }
+      }
+
+  /** Animate show transition for the education view */
+  private fun animateShowTooltipTransition() {
+    animator
+        ?.spring(DynamicAnimation.ALPHA, 1f)
+        ?.spring(DynamicAnimation.SCALE_X, 1f)
+        ?.spring(DynamicAnimation.SCALE_Y, 1f)
+        ?.start()
+  }
+
+  /** Animate hide transition for the education view */
+  private fun animateHideTooltipTransition(endActions: () -> Unit) {
+    animator
+        ?.spring(DynamicAnimation.ALPHA, 0f)
+        ?.spring(DynamicAnimation.SCALE_X, 0f)
+        ?.spring(DynamicAnimation.SCALE_Y, 0f)
+        ?.start()
+    endActions()
+  }
+
+  /** Remove education tooltip and clean up all relative properties */
+  private fun cleanUp() {
+    tooltipView = null
+    animator = null
+    popupWindow?.releaseView()
+    popupWindow = null
+  }
+
+  private fun createTooltipPopupWindow(
+      taskId: Int,
+      tooltipViewGlobalCoordinates: Point,
+      tooltipDimen: Size,
+      tooltipView: View,
+  ) {
+    popupWindow =
+        additionalSystemViewContainerFactory.create(
+            windowManagerWrapper =
+                WindowManagerWrapper(context.getSystemService(WindowManager::class.java)),
+            taskId = taskId,
+            x = tooltipViewGlobalCoordinates.x,
+            y = tooltipViewGlobalCoordinates.y,
+            width = tooltipDimen.width,
+            height = tooltipDimen.height,
+            flags =
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+            view = tooltipView)
+  }
+
+  private fun tooltipViewGlobalCoordinates(
+      tooltipViewGlobalCoordinates: Point,
+      arrowDirection: TooltipArrowDirection,
+      tooltipDimen: Size,
+  ): Point {
+    var tooltipX = tooltipViewGlobalCoordinates.x
+    var tooltipY = tooltipViewGlobalCoordinates.y
+
+    // Current values of [tooltipX]/[tooltipY] are the coordinates of tip of the arrow.
+    // Parameter x and y passed to [AdditionalSystemViewContainer] is the top left position of
+    // the window to be created. Hence we will need to move the coordinates left/up in order
+    // to position the tooltip correctly.
+    if (arrowDirection == TooltipArrowDirection.UP) {
+      // Arrow is placed at horizontal center on top edge of the tooltip. Hence decrement
+      // half of tooltip width from [tooltipX] to horizontally position the tooltip.
+      tooltipX -= tooltipDimen.width / 2
+    } else {
+      // 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
+    }
+    return Point(tooltipX, tooltipY)
+  }
+
+  private fun tooltipDimens(tooltipView: View, arrowDirection: TooltipArrowDirection): Size {
+    val tooltipBackground = tooltipView.requireViewById<LinearLayout>(R.id.tooltip_container)
+    val arrowView = tooltipView.requireViewById<ImageView>(R.id.arrow_icon)
+    tooltipBackground.measure(
+        /* widthMeasureSpec= */ UNSPECIFIED, /* heightMeasureSpec= */ UNSPECIFIED)
+    arrowView.measure(/* widthMeasureSpec= */ UNSPECIFIED, /* heightMeasureSpec= */ UNSPECIFIED)
+
+    var desiredWidth =
+        tooltipBackground.measuredWidth +
+            2 * loadDimensionPixelSize(R.dimen.desktop_windowing_education_tooltip_padding)
+    var desiredHeight =
+        tooltipBackground.measuredHeight +
+            2 * loadDimensionPixelSize(R.dimen.desktop_windowing_education_tooltip_padding)
+    if (arrowDirection == TooltipArrowDirection.UP) {
+      // desiredHeight currently does not account for the height of arrow, hence adding it.
+      desiredHeight += arrowView.height
+    } else {
+      // desiredWidth currently does not account for the width of arrow, hence adding it.
+      desiredWidth += arrowView.width
+    }
+
+    return Size(desiredWidth, desiredHeight)
+  }
+
+  private fun loadDimensionPixelSize(@DimenRes resourceId: Int): Int {
+    if (resourceId == Resources.ID_NULL) return 0
+    return context.resources.getDimensionPixelSize(resourceId)
+  }
+
+  /**
+   * The configuration for education view features:
+   *
+   * @property tooltipViewLayout Layout resource ID of the view to be used for education tooltip.
+   * @property tooltipViewGlobalCoordinates Global (screen) coordinates of the tip of the tooltip
+   *   arrow.
+   * @property tooltipText Text to be added to the TextView of tooltip.
+   * @property arrowDirection Direction of arrow of the tooltip.
+   * @property onEducationClickAction Lambda to be executed when the tooltip is clicked.
+   * @property onDismissAction Lambda to be executed when the tooltip is dismissed.
+   */
+  data class EducationViewConfig(
+      @LayoutRes val tooltipViewLayout: Int,
+      val tooltipViewGlobalCoordinates: Point,
+      val tooltipText: String,
+      val arrowDirection: TooltipArrowDirection,
+      val onEducationClickAction: () -> Unit,
+      val onDismissAction: () -> Unit,
+  )
+
+  /** Direction of arrow of the tooltip */
+  enum class TooltipArrowDirection {
+    UP,
+    LEFT,
+  }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 9c7d644..8c102eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -22,6 +22,7 @@
 import android.graphics.Color
 import android.graphics.Point
 import android.hardware.input.InputManager
+import android.os.Bundle
 import android.os.Handler
 import android.view.MotionEvent.ACTION_DOWN
 import android.view.SurfaceControl
@@ -29,7 +30,12 @@
 import android.view.View.OnClickListener
 import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
 import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
 import android.widget.ImageButton
+import androidx.core.view.ViewCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
 import com.android.internal.policy.SystemBarUtils
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
@@ -67,6 +73,20 @@
         captionView.setOnTouchListener(onCaptionTouchListener)
         captionHandle.setOnTouchListener(onCaptionTouchListener)
         captionHandle.setOnClickListener(onCaptionButtonClickListener)
+        captionHandle.accessibilityDelegate = object : View.AccessibilityDelegate() {
+            override fun sendAccessibilityEvent(host: View, eventType: Int) {
+                when (eventType) {
+                    AccessibilityEvent.TYPE_VIEW_HOVER_ENTER,
+                    AccessibilityEvent.TYPE_VIEW_HOVER_EXIT -> {
+                        // Caption Handle itself can't get a11y focus because it's under the status
+                        // bar, so pass through TYPE_VIEW_HOVER a11y events to the status bar
+                        // input layer, so that it can get a11y focus on the caption handle's behalf
+                        statusBarInputLayer?.view?.sendAccessibilityEvent(eventType)
+                    }
+                    else -> super.sendAccessibilityEvent(host, eventType)
+                }
+            }
+        }
     }
 
     override fun bindData(
@@ -134,9 +154,53 @@
             captionHandle.dispatchTouchEvent(event)
             return@setOnTouchListener true
         }
+        setupAppHandleA11y(view)
         windowManagerWrapper.updateViewLayout(view, lp)
     }
 
+    private fun setupAppHandleA11y(view: View) {
+        view.accessibilityDelegate = object : View.AccessibilityDelegate() {
+            override fun onInitializeAccessibilityNodeInfo(
+                host: View,
+                info: AccessibilityNodeInfo
+            ) {
+                // Allow the status bar input layer to be a11y clickable so it can interact with
+                // a11y services on behalf of caption handle (due to being under status bar)
+                super.onInitializeAccessibilityNodeInfo(host, info)
+                info.addAction(AccessibilityAction.ACTION_CLICK)
+                host.isClickable = true
+            }
+
+            override fun performAccessibilityAction(
+                host: View,
+                action: Int,
+                args: Bundle?
+            ): Boolean {
+                // Passthrough the a11y click action so the caption handle, so that app handle menu
+                // is opened on a11y click, similar to a real click
+                if (action == AccessibilityAction.ACTION_CLICK.id) {
+                    captionHandle.performClick()
+                }
+                return super.performAccessibilityAction(host, action, args)
+            }
+
+            override fun onPopulateAccessibilityEvent(host: View, event: AccessibilityEvent) {
+                super.onPopulateAccessibilityEvent(host, event)
+                // When the status bar input layer is focused, use the content description of the
+                // caption handle so that it appears as "App handle" and not "Unlabelled view"
+                if (event.eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
+                    event.text.add(captionHandle.contentDescription)
+                }
+            }
+        }
+
+        // Update a11y action text so that Talkback announces "Press double tap to open app handle
+        // menu" while focused on status bar input layer
+        ViewCompat.replaceAccessibilityAction(
+            view, AccessibilityActionCompat.ACTION_CLICK, "Open app handle menu", null
+        )
+    }
+
     private fun updateStatusBarInputLayer(globalPosition: Point) {
         statusBarInputLayer?.setPosition(
             SurfaceControl.Transaction(),
@@ -173,7 +237,8 @@
         return taskInfo.taskDescription
             ?.let { taskDescription ->
                 if (Color.alpha(taskDescription.statusBarColor) != 0 &&
-                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+                ) {
                     Color.valueOf(taskDescription.statusBarColor).luminance() < 0.5
                 } else {
                     taskDescription.systemBarsAppearance and APPEARANCE_LIGHT_STATUS_BARS == 0
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 4a8cabc..e996165 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
@@ -30,6 +30,7 @@
 import android.view.View
 import android.view.View.OnLongClickListener
 import android.view.ViewTreeObserver.OnGlobalLayoutListener
+import android.view.accessibility.AccessibilityEvent
 import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
@@ -47,7 +48,7 @@
 import com.android.internal.R.attr.materialColorSurfaceDim
 import com.android.window.flags.Flags.enableMinimizeButton
 import com.android.wm.shell.R
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
+import android.window.flags.DesktopModeFlags
 import com.android.wm.shell.windowdecor.MaximizeButtonView
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.OPACITY_100
@@ -157,7 +158,7 @@
         height: Int,
         isCaptionVisible: Boolean
     ) {
-        if (DesktopModeFlags.THEMED_APP_HEADERS.isEnabled(context)) {
+        if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
             bindDataWithThemedHeaders(taskInfo)
         } else {
             bindDataLegacy(taskInfo)
@@ -263,7 +264,11 @@
 
     override fun onHandleMenuOpened() {}
 
-    override fun onHandleMenuClosed() {}
+    override fun onHandleMenuClosed() {
+        openMenuButton.post {
+            openMenuButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+        }
+    }
 
     fun setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition: Boolean) {
         // If animating a task resize or reposition, cancel any running hover animations
@@ -309,6 +314,12 @@
         )
     }
 
+    fun requestAccessibilityFocus() {
+        maximizeWindowButton.post {
+            maximizeWindowButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+        }
+    }
+
     private fun getHeaderStyle(header: Header): HeaderStyle {
         return HeaderStyle(
             background = getHeaderBackground(header),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt
deleted file mode 100644
index 139e679..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHost.kt
+++ /dev/null
@@ -1,146 +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.windowdecor.viewhost
-
-import android.content.Context
-import android.content.res.Configuration
-import android.view.Display
-import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
-import android.view.View
-import android.view.WindowManager
-import android.view.WindowlessWindowManager
-import androidx.tracing.Trace
-import com.android.internal.annotations.VisibleForTesting
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-typealias SurfaceControlViewHostFactory =
-            (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
-
-/**
- * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost].
- *
- * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
- * any attempts to do will throw, which means that once a [View] is added using [updateView] or
- * [updateViewAsync], only its properties and binding may be changed, its children views may be
- * added, removed or changed and its [WindowManager.LayoutParams] may be changed.
- * It also supports asynchronously updating the view hierarchy using [updateViewAsync], in which
- * case the update work will be posted on the [ShellMainThread] with no delay.
- */
-class DefaultWindowDecorViewHost(
-    private val context: Context,
-    @ShellMainThread private val mainScope: CoroutineScope,
-    private val display: Display,
-    private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
-        SurfaceControlViewHost(c, d, wwm, s)
-    }
-) : WindowDecorViewHost {
-
-    private val rootSurface: SurfaceControl = SurfaceControl.Builder()
-            .setName("DefaultWindowDecorViewHost surface")
-            .setContainerLayer()
-            .setCallsite("DefaultWindowDecorViewHost#init")
-            .build()
-
-    private var wwm: WindowlessWindowManager? = null
-    @VisibleForTesting
-    var viewHost: SurfaceControlViewHost? = null
-    private var currentUpdateJob: Job? = null
-
-    override val surfaceControl: SurfaceControl
-        get() = rootSurface
-
-    override fun updateView(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration,
-        onDrawTransaction: SurfaceControl.Transaction?
-    ) {
-        Trace.beginSection("DefaultWindowDecorViewHost#updateView")
-        clearCurrentUpdateJob()
-        updateViewHost(view, attrs, configuration, onDrawTransaction)
-        Trace.endSection()
-    }
-
-    override fun updateViewAsync(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration
-    ) {
-        Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync")
-        clearCurrentUpdateJob()
-        currentUpdateJob = mainScope.launch {
-            updateViewHost(view, attrs, configuration, onDrawTransaction = null)
-        }
-        Trace.endSection()
-    }
-
-    override fun release(t: SurfaceControl.Transaction) {
-        clearCurrentUpdateJob()
-        viewHost?.release()
-        t.remove(rootSurface)
-    }
-
-    private fun updateViewHost(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration,
-        onDrawTransaction: SurfaceControl.Transaction?
-    ) {
-        Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost")
-        if (wwm == null) {
-            wwm = WindowlessWindowManager(configuration, rootSurface, null)
-        }
-        requireWindowlessWindowManager().setConfiguration(configuration)
-        if (viewHost == null) {
-            viewHost = surfaceControlViewHostFactory.invoke(
-                context,
-                display,
-                requireWindowlessWindowManager(),
-                "DefaultWindowDecorViewHost#updateViewHost"
-            )
-        }
-        onDrawTransaction?.let {
-            requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it)
-        }
-        if (requireViewHost().view == null) {
-            Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView")
-            requireViewHost().setView(view, attrs)
-            Trace.endSection()
-        } else {
-            check(requireViewHost().view == view) { "Changing view is not allowed" }
-            Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout")
-            requireViewHost().relayout(attrs)
-            Trace.endSection()
-        }
-        Trace.endSection()
-    }
-
-    private fun clearCurrentUpdateJob() {
-        currentUpdateJob?.cancel()
-        currentUpdateJob = null
-    }
-
-    private fun requireWindowlessWindowManager(): WindowlessWindowManager {
-        return wwm ?: error("Expected non-null windowless window manager")
-    }
-
-    private fun requireViewHost(): SurfaceControlViewHost {
-        return viewHost ?: error("Expected non-null view host")
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt
deleted file mode 100644
index 9997e8f..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostSupplier.kt
+++ /dev/null
@@ -1,38 +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.windowdecor.viewhost
-
-import android.content.Context
-import android.view.Display
-import android.view.SurfaceControl
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import kotlinx.coroutines.CoroutineScope
-
-/**
- * A supplier of [DefaultWindowDecorViewHost]s. It creates a new one every time one is requested.
- */
-class DefaultWindowDecorViewHostSupplier(
-    @ShellMainThread private val mainScope: CoroutineScope,
-) : WindowDecorViewHostSupplier<DefaultWindowDecorViewHost> {
-
-    override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost {
-        return DefaultWindowDecorViewHost(context, mainScope, display)
-    }
-
-    override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) {
-        viewHost.release(t)
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt
deleted file mode 100644
index 3fbaea8..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHost.kt
+++ /dev/null
@@ -1,49 +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.windowdecor.viewhost
-
-import android.content.res.Configuration
-import android.view.SurfaceControl
-import android.view.View
-import android.view.WindowManager
-import com.android.wm.shell.windowdecor.WindowDecoration
-
-/**
- * An interface for a utility that hosts a [WindowDecoration]'s [View] hierarchy under a
- * [SurfaceControl].
- */
-interface WindowDecorViewHost {
-    /** The surface where the underlying [View] hierarchy is being rendered. */
-    val surfaceControl: SurfaceControl
-
-    /** Synchronously update the view hierarchy of this view host. */
-    fun updateView(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration,
-        onDrawTransaction: SurfaceControl.Transaction?
-    )
-
-    /** Asynchronously update the view hierarchy of this view host. */
-    fun updateViewAsync(
-        view: View,
-        attrs: WindowManager.LayoutParams,
-        configuration: Configuration
-    )
-
-    /** Releases the underlying [View] hierarchy and removes the backing [SurfaceControl]. */
-    fun release(t: SurfaceControl.Transaction)
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt
deleted file mode 100644
index 0e23584..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewhost/WindowDecorViewHostSupplier.kt
+++ /dev/null
@@ -1,38 +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.windowdecor.viewhost
-
-import android.content.Context
-import android.view.Display
-import android.view.SurfaceControl
-
-/**
- * An interface for a supplier of [WindowDecorViewHost]s.
- */
-interface WindowDecorViewHostSupplier<T : WindowDecorViewHost> {
-    /** Acquire a [WindowDecorViewHost]. */
-    fun acquire(context: Context, display: Display): T
-
-    /**
-     * Release a [WindowDecorViewHost] when it is no longer used.
-     *
-     * @param viewHost the [WindowDecorViewHost] to release
-     * @param t a transaction that may be used to remove any underlying backing [SurfaceControl]
-     *          that are hosting this [WindowDecorViewHost]. The supplier is not expected to apply
-     *          the transaction. It should be applied by the owner of this supplier.
-     */
-    fun release(viewHost: T, t: SurfaceControl.Transaction)
-}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt
index 717ea30..ce235d4 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithMaxDesktopWindows.kt
@@ -72,7 +72,6 @@
 
     @Test
     open fun startMediaProjection() {
-        // TODO(b/366455106) - handle max task Limit
         mediaProjectionAppHelper.startSingleAppMediaProjection(wmHelper, targetApp)
         mailApp.launchViaIntent(wmHelper)
         simpleApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithDisplayRotations.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithDisplayRotations.kt
index 1573b58..f5fb4ce 100644
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithDisplayRotations.kt
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartAppMediaProjectionWithDisplayRotations.kt
@@ -20,13 +20,12 @@
 import android.platform.test.annotations.Postsubmit
 import android.tools.NavBar
 import android.tools.Rotation
-import android.tools.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.device.apphelpers.CalculatorAppHelper
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
 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.StartMediaProjectionAppHelper
 import com.android.wm.shell.Utils
 import org.junit.After
@@ -47,8 +46,7 @@
 
     private val initialRotation = Rotation.ROTATION_0
     private val targetApp = CalculatorAppHelper(instrumentation)
-    private val mediaProjectionAppHelper = StartMediaProjectionAppHelper(instrumentation)
-    private val testApp = DesktopModeAppHelper(mediaProjectionAppHelper)
+    private val testApp = StartMediaProjectionAppHelper(instrumentation)
 
     @Rule
     @JvmField
@@ -63,7 +61,7 @@
 
     @Test
     open fun startMediaProjectionAndRotate() {
-        mediaProjectionAppHelper.startSingleAppMediaProjection(wmHelper, targetApp)
+        testApp.startSingleAppMediaProjection(wmHelper, targetApp)
         wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
 
         ChangeDisplayOrientationRule.setRotation(Rotation.ROTATION_90)
diff --git a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithDisplayRotations.kt b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithDisplayRotations.kt
index e80a895..28f3cc7 100644
--- a/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithDisplayRotations.kt
+++ b/libs/WindowManager/Shell/tests/e2e/mediaprojection/scenarios/src/com/android/wm/shell/scenarios/StartScreenMediaProjectionWithDisplayRotations.kt
@@ -25,7 +25,6 @@
 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.StartMediaProjectionAppHelper
 import com.android.wm.shell.Utils
 import org.junit.After
@@ -45,8 +44,7 @@
     val device = UiDevice.getInstance(instrumentation)
 
     private val initialRotation = Rotation.ROTATION_0
-    private val mediaProjectionAppHelper = StartMediaProjectionAppHelper(instrumentation)
-    private val testApp = DesktopModeAppHelper(mediaProjectionAppHelper)
+    private val testApp = StartMediaProjectionAppHelper(instrumentation)
 
     @Rule
     @JvmField
@@ -60,7 +58,7 @@
 
     @Test
     open fun startMediaProjectionAndRotate() {
-        mediaProjectionAppHelper.startEntireScreenMediaProjection(wmHelper)
+        testApp.startEntireScreenMediaProjection(wmHelper)
         wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
 
         ChangeDisplayOrientationRule.setRotation(Rotation.ROTATION_90)
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
index c7cbc3e..22adf6c 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
@@ -48,6 +48,7 @@
     fun setup() {
         tapl.workspace.switchToOverview().dismissAllTasks()
 
+        tapl.setExpectedRotationCheckEnabled(false)
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index e514dc3..f01ed84 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -39,6 +39,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.TaskInfo;
@@ -599,6 +600,18 @@
     }
 
     @Test
+    public void testRecentTasks_visibilityChanges_notFreeForm_shouldNotNotifyTaskController() {
+        RunningTaskInfo task1_visible = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN);
+        mOrganizer.onTaskAppeared(task1_visible, /* leash= */ null);
+        RunningTaskInfo task1_hidden = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN);
+        task1_hidden.isVisible = false;
+
+        mOrganizer.onTaskInfoChanged(task1_hidden);
+
+        verify(mRecentTasksController, never()).onTaskRunningInfoChanged(task1_hidden);
+    }
+
+    @Test
     public void testRecentTasks_windowingModeChanges_shouldNotifyTaskController() {
         RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN);
         mOrganizer.onTaskAppeared(task1, /* leash= */ null);
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 a2df22c..40b685c 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
@@ -17,6 +17,7 @@
 package com.android.wm.shell;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
@@ -45,6 +46,9 @@
         // Disable protolog tool when running the tests from studio
         ProtoLog.REQUIRE_PROTOLOGTOOL = false;
 
+        // Make sure ProtoLog is initialized before any logging occurs.
+        ProtoLog.init();
+
         MockitoAnnotations.initMocks(this);
         final Context context =
                 InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 24f4d92..e6bd05b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -47,6 +47,8 @@
     private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
     private final Point mPositionInParent = new Point();
     private boolean mIsVisible = false;
+    private boolean mIsTopActivityTransparent = false;
+    private int mNumActivities = 1;
     private long mLastActiveTime;
 
     public static WindowContainerToken createMockWCToken() {
@@ -113,6 +115,16 @@
         return this;
     }
 
+    public TestRunningTaskInfoBuilder setTopActivityTransparent(boolean isTopActivityTransparent) {
+        mIsTopActivityTransparent = isTopActivityTransparent;
+        return this;
+    }
+
+    public TestRunningTaskInfoBuilder setNumActivities(int numActivities) {
+        mNumActivities = numActivities;
+        return this;
+    }
+
     public TestRunningTaskInfoBuilder setLastActiveTime(long lastActiveTime) {
         mLastActiveTime = lastActiveTime;
         return this;
@@ -134,6 +146,8 @@
                 mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null;
         info.positionInParent = mPositionInParent;
         info.isVisible = mIsVisible;
+        info.isTopActivityTransparent = mIsTopActivityTransparent;
+        info.numActivities = mNumActivities;
         info.lastActiveTime = mLastActiveTime;
         return info;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/OWNERS
new file mode 100644
index 0000000..622f837
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1168918
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/OWNERS
new file mode 100644
index 0000000..553540c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 929241
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/OWNERS
new file mode 100644
index 0000000..983e878
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 555586
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS
new file mode 100644
index 0000000..5b05af9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 970984
+# includes OWNERS from parent directories
\ No newline at end of file
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 b14f163..628c9cdd 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
@@ -41,12 +41,22 @@
 import com.android.wm.shell.common.TaskStackListenerImpl
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
+import com.android.wm.shell.desktopmode.persistence.Desktop
+import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertTrue
 import kotlin.test.assertNotNull
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -73,6 +83,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
 @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE)
 class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
     @JvmField @Rule val setFlagsRule = SetFlagsRule()
@@ -82,16 +93,19 @@
     @Mock lateinit var transitions: Transitions
     @Mock lateinit var resizeTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
     @Mock lateinit var taskStackListener: TaskStackListenerImpl
+    @Mock lateinit var persistentRepository: DesktopPersistentRepository
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var handler: DesktopActivityOrientationChangeHandler
     private lateinit var shellInit: ShellInit
     private lateinit var taskRepository: DesktopModeTaskRepository
+    private lateinit var testScope: CoroutineScope
     // Mock running tasks are registered here so we can get the list from mock shell task organizer.
     private val runningTasks = mutableListOf<RunningTaskInfo>()
 
     @Before
     fun setUp() {
+        Dispatchers.setMain(StandardTestDispatcher())
         mockitoSession =
             mockitoSession()
                 .strictness(Strictness.LENIENT)
@@ -99,10 +113,15 @@
                 .startMocking()
         doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
+        testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
         shellInit = spy(ShellInit(testExecutor))
-        taskRepository = DesktopModeTaskRepository()
+        taskRepository =
+            DesktopModeTaskRepository(context, shellInit, persistentRepository, testScope)
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
         whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+        whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
+            Desktop.getDefaultInstance()
+        )
 
         handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
             taskStackListener, resizeTransitionHandler, taskRepository)
@@ -115,6 +134,7 @@
         mockitoSession.finishMocking()
 
         runningTasks.clear()
+        testScope.cancel()
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index d3404f7..bc40d89 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -17,27 +17,70 @@
 package com.android.wm.shell.desktopmode
 
 import android.graphics.Rect
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
+import android.util.ArraySet
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.Display.INVALID_DISPLAY
 import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.persistence.Desktop
+import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
+import com.android.wm.shell.sysui.ShellInit
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.fail
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.spy
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
 class DesktopModeTaskRepositoryTest : ShellTestCase() {
 
     private lateinit var repo: DesktopModeTaskRepository
+    private lateinit var shellInit: ShellInit
+    private lateinit var datastoreScope: CoroutineScope
+
+    @Mock private lateinit var testExecutor: ShellExecutor
+    @Mock private lateinit var persistentRepository: DesktopPersistentRepository
 
     @Before
     fun setUp() {
-        repo = DesktopModeTaskRepository()
+        Dispatchers.setMain(StandardTestDispatcher())
+        datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+        shellInit = spy(ShellInit(testExecutor))
+
+        repo = DesktopModeTaskRepository(context, shellInit, persistentRepository, datastoreScope)
+        whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
+            Desktop.getDefaultInstance()
+        )
+        shellInit.init()
+    }
+
+    @After
+    fun tearDown() {
+        datastoreScope.cancel()
     }
 
     @Test
@@ -455,6 +498,44 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun addOrMoveFreeformTaskToTop_noTaskExists_persistenceEnabled_addsToTop() =
+        runTest(StandardTestDispatcher()) {
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+
+            val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
+            assertThat(tasks).containsExactly(7, 6, 5).inOrder()
+            inOrder(persistentRepository).run {
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf(5)
+                    )
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf(6, 5)
+                    )
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf(7, 6, 5)
+                    )
+            }
+    }
+
+    @Test
     fun addOrMoveFreeformTaskToTop_alreadyExists_movesToTop() {
         repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
         repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
@@ -480,6 +561,55 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun minimizeTask_persistenceEnabled_taskIsPersistedAsMinimized() =
+        runTest(StandardTestDispatcher()) {
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 7)
+
+            repo.minimizeTask(displayId = 0, taskId = 6)
+
+            val tasks = repo.getFreeformTasksInZOrder(DEFAULT_DISPLAY)
+            assertThat(tasks).containsExactly(7, 6, 5).inOrder()
+            assertThat(repo.isMinimizedTask(taskId = 6)).isTrue()
+            inOrder(persistentRepository).run {
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf(5)
+                    )
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf(6, 5)
+                    )
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf(7, 6, 5)
+                    )
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(),
+                        minimizedTasks = ArraySet(arrayOf(6)),
+                        freeformTasksInZOrder = arrayListOf(7, 6, 5)
+                    )
+            }
+    }
+
+    @Test
     fun addOrMoveFreeformTaskToTop_taskIsUnminimized_noop() {
         repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 5)
         repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, 6)
@@ -503,6 +633,33 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun removeFreeformTask_invalidDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
+        runTest(StandardTestDispatcher()) {
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+
+            repo.removeFreeformTask(INVALID_DISPLAY, taskId = 1)
+
+            verify(persistentRepository)
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = arrayListOf(1)
+                )
+            verify(persistentRepository)
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = ArrayList()
+                )
+        }
+    }
+
+    @Test
     fun removeFreeformTask_validDisplay_removesTaskFromFreeformTasks() {
         repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
 
@@ -513,6 +670,33 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun removeFreeformTask_validDisplay_persistenceEnabled_removesTaskFromFreeformTasks() {
+        runTest(StandardTestDispatcher()) {
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+
+            repo.removeFreeformTask(DEFAULT_DISPLAY, taskId = 1)
+
+            verify(persistentRepository)
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = arrayListOf(1)
+                )
+            verify(persistentRepository)
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = ArrayList()
+                )
+        }
+    }
+
+    @Test
     fun removeFreeformTask_validDisplay_differentDisplay_doesNotRemovesTask() {
         repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
 
@@ -523,6 +707,33 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun removeFreeformTask_validDisplayButDifferentDisplay_persistenceEnabled_doesNotRemoveTask() {
+        runTest(StandardTestDispatcher()) {
+            repo.addOrMoveFreeformTaskToTop(DEFAULT_DISPLAY, taskId = 1)
+
+            repo.removeFreeformTask(SECOND_DISPLAY, taskId = 1)
+
+            verify(persistentRepository)
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = arrayListOf(1)
+                )
+            verify(persistentRepository, never())
+                .addOrUpdateDesktop(
+                    DEFAULT_USER_ID,
+                    DEFAULT_DESKTOP_ID,
+                    visibleTasks = ArraySet(),
+                    minimizedTasks = ArraySet(),
+                    freeformTasksInZOrder = ArrayList()
+                )
+        }
+    }
+
+    @Test
     fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
         val taskId = 1
         repo.addActiveTask(THIRD_DISPLAY, taskId)
@@ -709,5 +920,7 @@
     companion object {
         const val SECOND_DISPLAY = 1
         const val THIRD_DISPLAY = 345
+        private const val DEFAULT_USER_ID = 1000
+        private const val DEFAULT_DESKTOP_ID = 0
     }
 }
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 8f20841..29aea00 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
@@ -93,6 +93,8 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.desktopmode.persistence.Desktop
+import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.draganddrop.DragAndDropController
 import com.android.wm.shell.recents.RecentTasksController
 import com.android.wm.shell.recents.RecentsTransitionHandler
@@ -117,6 +119,14 @@
 import junit.framework.Assert.assertTrue
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
@@ -148,6 +158,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
 @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
 class DesktopTasksControllerTest : ShellTestCase() {
 
@@ -183,6 +194,7 @@
   @Mock private lateinit var mockSurface: SurfaceControl
   @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
   @Mock private lateinit var mockHandler: Handler
+  @Mock lateinit var persistentRepository: DesktopPersistentRepository
 
   private lateinit var mockitoSession: StaticMockitoSession
   private lateinit var controller: DesktopTasksController
@@ -190,6 +202,7 @@
   private lateinit var taskRepository: DesktopModeTaskRepository
   private lateinit var desktopTasksLimiter: DesktopTasksLimiter
   private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
+  private lateinit var testScope: CoroutineScope
 
   private val shellExecutor = TestShellExecutor()
 
@@ -207,6 +220,7 @@
 
   @Before
   fun setUp() {
+    Dispatchers.setMain(StandardTestDispatcher())
     mockitoSession =
         mockitoSession()
             .strictness(Strictness.LENIENT)
@@ -214,8 +228,9 @@
             .startMocking()
     doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
+    testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
     shellInit = spy(ShellInit(testExecutor))
-    taskRepository = DesktopModeTaskRepository()
+    taskRepository = DesktopModeTaskRepository(context, shellInit, persistentRepository, testScope)
     desktopTasksLimiter =
         DesktopTasksLimiter(
             transitions,
@@ -233,6 +248,9 @@
     whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
       (i.arguments.first() as Rect).set(STABLE_BOUNDS)
     }
+    whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
+      Desktop.getDefaultInstance()
+    )
 
     val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
     tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
@@ -287,6 +305,7 @@
     mockitoSession.finishMocking()
 
     runningTasks.clear()
+    testScope.cancel()
   }
 
   @Test
@@ -2067,16 +2086,13 @@
   }
 
   @Test
-  @EnableFlags(
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
-  )
-  fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_withBackNav_removesTask() {
+  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
+  fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_removesTask() {
     val task = setUpFreeformTask()
 
     val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
 
-    assertNotNull(result, "Should handle request").assertRemoveAt(0, task.token)
+    assertNull(result, "Should not handle request")
   }
 
   @Test
@@ -2118,26 +2134,8 @@
   }
 
   @Test
-  @EnableFlags(
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
-  )
-  fun handleRequest_backTransition_singleTask_withWallpaper_withBackNav_removesWallpaperAndTask() {
-    val task = setUpFreeformTask()
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-    result.assertRemoveAt(index = 1, task.token)
-  }
-
-  @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-  fun handleRequest_backTransition_singleTaskWithToken_noBackNav_removesWallpaper() {
+  fun handleRequest_backTransition_singleTaskWithToken_removesWallpaper() {
     val task = setUpFreeformTask()
     val wallpaperToken = MockToken().token()
 
@@ -2164,23 +2162,7 @@
   }
 
   @Test
-  @EnableFlags(
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
-  )
-  fun handleRequest_backTransition_multipleTasks_withWallpaper_withBackNav_removesTask() {
-    val task1 = setUpFreeformTask()
-    setUpFreeformTask()
-
-    taskRepository.wallpaperActivityToken = MockToken().token()
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, task1.token)
-  }
-
-  @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
   fun handleRequest_backTransition_multipleTasks_noBackNav_doesNotHandle() {
     val task1 = setUpFreeformTask()
     setUpFreeformTask()
@@ -2207,29 +2189,11 @@
 
     // Should create remove wallpaper transaction
     assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-    result.assertRemoveAt(index = 1, task1.token)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-  fun handleRequest_backTransition_multipleTasksSingleNonClosing_noBackNav_removesWallpaper() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
   }
 
   @Test
   @EnableFlags(
     Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
   )
   fun handleRequest_backTransition_multipleTasksSingleNonMinimized_removesWallpaperAndTask() {
     val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -2242,23 +2206,6 @@
 
     // Should create remove wallpaper transaction
     assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
-    result.assertRemoveAt(index = 1, task1.token)
-  }
-
-  @Test
-  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
-  fun handleRequest_backTransition_multipleTasksSingleNonMinimized_noBackNav_removesWallpaper() {
-    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
-    val wallpaperToken = MockToken().token()
-
-    taskRepository.wallpaperActivityToken = wallpaperToken
-    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
-    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
-
-    // Should create remove wallpaper transaction
-    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
   }
 
   @Test
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 61d03ca..045e077 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
@@ -35,13 +35,23 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.util.StubTransaction
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -49,6 +59,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.any
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.`when`
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.verify
@@ -62,6 +73,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
 class DesktopTasksLimiterTest : ShellTestCase() {
 
     @JvmField
@@ -72,19 +84,26 @@
     @Mock lateinit var transitions: Transitions
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
     @Mock lateinit var handler: Handler
+    @Mock lateinit var testExecutor: ShellExecutor
+    @Mock lateinit var persistentRepository: DesktopPersistentRepository
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var desktopTasksLimiter: DesktopTasksLimiter
     private lateinit var desktopTaskRepo: DesktopModeTaskRepository
+    private lateinit var shellInit: ShellInit
+    private lateinit var testScope: CoroutineScope
 
     @Before
     fun setUp() {
         mockitoSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
                 .spyStatic(DesktopModeStatus::class.java).startMocking()
         doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(any()) }
+        shellInit = spy(ShellInit(testExecutor))
+        Dispatchers.setMain(StandardTestDispatcher())
+        testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
 
-        desktopTaskRepo = DesktopModeTaskRepository()
-
+        desktopTaskRepo =
+            DesktopModeTaskRepository(context, shellInit, persistentRepository, testScope)
         desktopTasksLimiter =
             DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT,
                 interactionJankMonitor, mContext, handler)
@@ -93,6 +112,7 @@
     @After
     fun tearDown() {
         mockitoSession.finishMocking()
+        testScope.cancel()
     }
 
     @Test
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
new file mode 100644
index 0000000..c989d16
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -0,0 +1,138 @@
+/*
+ * 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
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.IWindowContainerToken
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
+import android.window.WindowContainerToken
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mockito
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+class DesktopTasksTransitionObserverTest {
+
+    @JvmField
+    @Rule
+    val extendedMockitoRule =
+        ExtendedMockitoRule.Builder(this)
+            .mockStatic(DesktopModeStatus::class.java)
+            .build()!!
+
+    private val testExecutor = mock<ShellExecutor>()
+    private val mockShellInit = mock<ShellInit>()
+    private val transitions = mock<Transitions>()
+    private val context = mock<Context>()
+    private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+    private val taskRepository = mock<DesktopModeTaskRepository>()
+
+    private lateinit var transitionObserver: DesktopTasksTransitionObserver
+    private lateinit var shellInit: ShellInit
+
+    @Before
+    fun setup() {
+        whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+        shellInit = spy(ShellInit(testExecutor))
+
+        transitionObserver =
+            DesktopTasksTransitionObserver(
+                context, taskRepository, transitions, shellTaskOrganizer, shellInit
+            )
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun backNavigation_taskMinimized() {
+        val task = createTaskInfo(1)
+        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
+
+        transitionObserver.onTransitionReady(
+            transition = mock(),
+            info =
+            createBackNavigationTransition(task),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository).minimizeTask(task.displayId, task.taskId)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+    fun backNavigation_nullTaskInfo_taskNotMinimized() {
+        val task = createTaskInfo(1)
+        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
+
+        transitionObserver.onTransitionReady(
+            transition = mock(),
+            info =
+            createBackNavigationTransition(null),
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId)
+    }
+
+    private fun createBackNavigationTransition(
+        task: RunningTaskInfo?
+    ): TransitionInfo {
+        return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
+            addChange(
+                Change(mock(), mock()).apply {
+                    mode = TRANSIT_TO_BACK
+                    parent = null
+                    taskInfo = task
+                    flags = flags
+                }
+            )
+        }
+    }
+
+    private fun createTaskInfo(id: Int) =
+        RunningTaskInfo().apply {
+            taskId = id
+            displayId = DEFAULT_DISPLAY
+            configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+            token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
+            baseIntent = Intent().apply {
+                component = ComponentName("package", "component.name")
+            }
+        }
+}
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 497d0e5..d9387d2 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
@@ -35,6 +35,7 @@
 import java.util.function.Supplier
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -212,6 +213,60 @@
     }
 
     @Test
+    fun isHomeChange_withoutTaskInfo_returnsFalse() {
+        val change =
+            TransitionInfo.Change(mock(), homeTaskLeash).apply {
+                parent = null
+                taskInfo = null
+            }
+
+        assertFalse(defaultHandler.isHomeChange(change))
+        assertFalse(springHandler.isHomeChange(change))
+    }
+
+    @Test
+    fun isHomeChange_withStandardActivityTaskInfo_returnsFalse() {
+        val change =
+            TransitionInfo.Change(mock(), homeTaskLeash).apply {
+                parent = null
+                taskInfo =
+                    TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_STANDARD).build()
+            }
+
+        assertFalse(defaultHandler.isHomeChange(change))
+        assertFalse(springHandler.isHomeChange(change))
+    }
+
+    @Test
+    fun isHomeChange_withHomeActivityTaskInfo_returnsTrue() {
+        val change =
+            TransitionInfo.Change(mock(), homeTaskLeash).apply {
+                parent = null
+                taskInfo = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+            }
+
+        assertTrue(defaultHandler.isHomeChange(change))
+        assertTrue(springHandler.isHomeChange(change))
+    }
+
+    @Test
+    fun isHomeChange_withSingleTranslucentHomeActivityTaskInfo_returnsFalse() {
+        val change =
+            TransitionInfo.Change(mock(), homeTaskLeash).apply {
+                parent = null
+                taskInfo =
+                    TestRunningTaskInfoBuilder()
+                        .setActivityType(ACTIVITY_TYPE_HOME)
+                        .setTopActivityTransparent(true)
+                        .setNumActivities(1)
+                        .build()
+            }
+
+        assertFalse(defaultHandler.isHomeChange(change))
+        assertFalse(springHandler.isHomeChange(change))
+    }
+
+    @Test
     fun cancelDragToDesktop_startWasReady_cancel() {
         startDrag(defaultHandler)
 
@@ -343,6 +398,8 @@
         // Should show dragged task layer in start and finish transaction
         verify(mergedStartTransaction).show(draggedTaskLeash)
         verify(playingFinishTransaction).show(draggedTaskLeash)
+        // Should update the dragged task layer
+        verify(mergedStartTransaction).setLayer(eq(draggedTaskLeash), anyInt())
         // Should merge animation
         verify(finishCallback).onTransitionFinished(null)
     }
@@ -373,6 +430,8 @@
         // Should show dragged task layer in start and finish transaction
         verify(mergedStartTransaction).show(draggedTaskLeash)
         verify(playingFinishTransaction).show(draggedTaskLeash)
+        // Should update the dragged task layer
+        verify(mergedStartTransaction).setLayer(eq(draggedTaskLeash), anyInt())
         // Should hide home task leash in finish transaction
         verify(playingFinishTransaction).hide(homeTaskLeash)
         // Should merge animation
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/OWNERS
new file mode 100644
index 0000000..553540c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 929241
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
new file mode 100644
index 0000000..9b9703f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.persistence
+
+import android.content.Context
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.util.ArraySet
+import android.view.Display.DEFAULT_DISPLAY
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.dataStoreFile
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+class DesktopPersistentRepositoryTest : ShellTestCase() {
+    private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
+    private lateinit var testDatastore: DataStore<DesktopPersistentRepositories>
+    private lateinit var datastoreRepository: DesktopPersistentRepository
+    private lateinit var datastoreScope: CoroutineScope
+
+    @Before
+    fun setUp() {
+        Dispatchers.setMain(StandardTestDispatcher())
+        datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+        testDatastore =
+            DataStoreFactory.create(
+                serializer =
+                    DesktopPersistentRepository.Companion.DesktopPersistentRepositoriesSerializer,
+                scope = datastoreScope) {
+                    testContext.dataStoreFile(DESKTOP_REPOSITORY_STATES_DATASTORE_TEST_FILE)
+                }
+        datastoreRepository = DesktopPersistentRepository(testDatastore)
+    }
+
+    @After
+    fun tearDown() {
+        File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
+            .deleteRecursively()
+
+        datastoreScope.cancel()
+    }
+
+    @Test
+    fun readRepository_returnsCorrectDesktop() {
+        runTest(StandardTestDispatcher()) {
+            val task = createDesktopTask(1)
+            val desk = createDesktop(task)
+            val repositoryState =
+                DesktopRepositoryState.newBuilder().putDesktop(DEFAULT_DESKTOP_ID, desk)
+            val DesktopPersistentRepositories =
+                DesktopPersistentRepositories.newBuilder()
+                    .putDesktopRepoByUser(DEFAULT_USER_ID, repositoryState.build())
+                    .build()
+            testDatastore.updateData { DesktopPersistentRepositories }
+
+            val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+
+            assertThat(actualDesktop).isEqualTo(desk)
+        }
+    }
+
+    @Test
+    fun addOrUpdateTask_addNewTaskToDesktop() {
+        runTest(StandardTestDispatcher()) {
+            // Create a basic repository state
+            val task = createDesktopTask(1)
+            val DesktopPersistentRepositories = createRepositoryWithOneDesk(task)
+            testDatastore.updateData { DesktopPersistentRepositories }
+            // Create a new state to be initialized
+            val visibleTasks = ArraySet(listOf(1, 2))
+            val minimizedTasks = ArraySet<Int>()
+            val freeformTasksInZOrder = ArrayList(listOf(2, 1))
+
+            // Update with new state
+            datastoreRepository.addOrUpdateDesktop(
+                visibleTasks = visibleTasks,
+                minimizedTasks = minimizedTasks,
+                freeformTasksInZOrder = freeformTasksInZOrder)
+
+            val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+            assertThat(actualDesktop.tasksByTaskIdMap).hasSize(2)
+            assertThat(actualDesktop.getZOrderedTasks(0)).isEqualTo(2)
+        }
+    }
+
+    @Test
+    fun addOrUpdateTask_changeTaskStateToMinimize_taskStateIsMinimized() {
+        runTest(StandardTestDispatcher()) {
+            val task = createDesktopTask(1)
+            val DesktopPersistentRepositories = createRepositoryWithOneDesk(task)
+            testDatastore.updateData { DesktopPersistentRepositories }
+            // Create a new state to be initialized
+            val visibleTasks = ArraySet(listOf(1))
+            val minimizedTasks = ArraySet(listOf(1))
+            val freeformTasksInZOrder = ArrayList(listOf(1))
+
+            // Update with new state
+            datastoreRepository.addOrUpdateDesktop(
+                visibleTasks = visibleTasks,
+                minimizedTasks = minimizedTasks,
+                freeformTasksInZOrder = freeformTasksInZOrder)
+
+            val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+            assertThat(actualDesktop.tasksByTaskIdMap[task.taskId]?.desktopTaskState)
+                .isEqualTo(DesktopTaskState.MINIMIZED)
+        }
+    }
+
+    @Test
+    fun removeTask_previouslyAddedTaskIsRemoved() {
+        runTest(StandardTestDispatcher()) {
+            val task = createDesktopTask(1)
+            val DesktopPersistentRepositories = createRepositoryWithOneDesk(task)
+            testDatastore.updateData { DesktopPersistentRepositories }
+            // Create a new state to be initialized
+            val visibleTasks = ArraySet<Int>()
+            val minimizedTasks = ArraySet<Int>()
+            val freeformTasksInZOrder = ArrayList<Int>()
+
+            // Update with new state
+            datastoreRepository.addOrUpdateDesktop(
+                visibleTasks = visibleTasks,
+                minimizedTasks = minimizedTasks,
+                freeformTasksInZOrder = freeformTasksInZOrder)
+
+            val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+            assertThat(actualDesktop.tasksByTaskIdMap).isEmpty()
+            assertThat(actualDesktop.zOrderedTasksList).isEmpty()
+        }
+    }
+
+    private companion object {
+        const val DESKTOP_REPOSITORY_STATES_DATASTORE_TEST_FILE = "desktop_repo_test.pb"
+        const val DEFAULT_USER_ID = 1000
+        const val DEFAULT_DESKTOP_ID = 0
+
+        fun createRepositoryWithOneDesk(task: DesktopTask): DesktopPersistentRepositories {
+            val desk = createDesktop(task)
+            val repositoryState =
+                DesktopRepositoryState.newBuilder().putDesktop(DEFAULT_DESKTOP_ID, desk)
+            val DesktopPersistentRepositories =
+                DesktopPersistentRepositories.newBuilder()
+                    .putDesktopRepoByUser(DEFAULT_USER_ID, repositoryState.build())
+                    .build()
+            return DesktopPersistentRepositories
+        }
+
+        fun createDesktop(task: DesktopTask): Desktop? =
+            Desktop.newBuilder()
+                .setDisplayId(DEFAULT_DISPLAY)
+                .addZOrderedTasks(task.taskId)
+                .putTasksByTaskId(task.taskId, task)
+                .build()
+
+        fun createDesktopTask(
+            taskId: Int,
+            state: DesktopTaskState = DesktopTaskState.VISIBLE
+        ): DesktopTask =
+            DesktopTask.newBuilder().setTaskId(taskId).setDesktopTaskState(state).build()
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/OWNERS
new file mode 100644
index 0000000..cb12401
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1214056
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/OWNERS
new file mode 100644
index 0000000..553540c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 929241
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OWNERS
new file mode 100644
index 0000000..b66cfc3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 785166
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/OWNERS
new file mode 100644
index 0000000..ad3ca73
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316251
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/OWNERS
new file mode 100644
index 0000000..ad3ca73
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316251
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/OWNERS
new file mode 100644
index 0000000..aa019cc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1199235
+# includes OWNERS from parent directories
\ No newline at end of file
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 bc9b44e..0e5efa6 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
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager
 import android.app.WindowConfiguration
-import android.content.Context
 import android.os.IBinder
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
@@ -60,7 +59,6 @@
 
     @JvmField @Rule val setFlagsRule = SetFlagsRule()
 
-    @Mock private lateinit var context: Context
     @Mock private lateinit var shellInit: ShellInit
     @Mock lateinit var testExecutor: ShellExecutor
     @Mock private lateinit var transitionsLazy: Lazy<Transitions>
@@ -74,7 +72,7 @@
         MockitoAnnotations.initMocks(this)
         shellInit = Mockito.spy(ShellInit(testExecutor))
         whenever(transitionsLazy.get()).thenReturn(transitions)
-        transitionObserver = TaskStackTransitionObserver(context, transitionsLazy, shellInit)
+        transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
             verify(shellInit)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
deleted file mode 100644
index 571bdd4..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
+++ /dev/null
@@ -1,347 +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.shared.desktopmode
-
-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.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
-import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
-import com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION
-import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.Companion.convertToToggleOverrideWithFallback
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_ON
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVITY
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Test class for [DesktopModeFlags]
- *
- * Usage: atest WMShellUnitTests:DesktopModeFlagsTest
- */
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class DesktopModeFlagsTest : ShellTestCase() {
-
-  @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
-  @After
-  fun tearDown() {
-    resetToggleOverrideCache()
-  }
-
-  // TODO(b/348193756): Add tests
-  // isEnabled_flagNotOverridable_overrideOff_featureFlagOn_returnsTrue and
-  // isEnabled_flagNotOverridable_overrideOn_featureFlagOff_returnsFalse after adding non
-  // overridable flags to DesktopModeFlags.
-
-  @Test
-  @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
-    setOverride(OVERRIDE_OFF.setting)
-
-    // In absence of dev options, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
-    setOverride(OVERRIDE_ON.setting)
-
-    // In absence of dev options, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_overrideUnset_featureFlagOn_returnsTrue() {
-    setOverride(OVERRIDE_UNSET.setting)
-
-    // For overridableFlag, for unset overrides, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_overrideUnset_featureFlagOff_returnsFalse() {
-    setOverride(OVERRIDE_UNSET.setting)
-
-    // For overridableFlag, for unset overrides, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noOverride_featureFlagOn_returnsTrue() {
-    setOverride(null)
-
-    // For overridableFlag, in absence of overrides, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_noOverride_featureFlagOff_returnsFalse() {
-    setOverride(null)
-
-    // For overridableFlag, in absence of overrides, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_unrecognizableOverride_featureFlagOn_returnsTrue() {
-    setOverride(-2)
-
-    // For overridableFlag, for recognizable overrides, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_unrecognizableOverride_featureFlagOff_returnsFalse() {
-    setOverride(-2)
-
-    // For overridableFlag, for recognizable overrides, follow flag
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_overrideOff_featureFlagOn_returnsFalse() {
-    setOverride(OVERRIDE_OFF.setting)
-
-    // For overridableFlag, follow override if they exist
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_overrideOn_featureFlagOff_returnsTrue() {
-    setOverride(OVERRIDE_ON.setting)
-
-    // For overridableFlag, follow override if they exist
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {
-    setOverride(OVERRIDE_OFF.setting)
-
-    // For overridableFlag, follow override if they exist
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-
-    setOverride(OVERRIDE_ON.setting)
-
-    // Keep overrides constant through the process
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {
-    setOverride(OVERRIDE_ON.setting)
-
-    // For overridableFlag, follow override if they exist
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-
-    setOverride(OVERRIDE_OFF.setting)
-
-    // Keep overrides constant through the process
-    assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(
-      FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-      FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-      FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagEnabled_overrideUnset_featureFlagOn_returnsTrue() {
-    setOverride(OVERRIDE_UNSET.setting)
-
-    // For unset overrides, follow flag
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagEnabled_overrideUnset_featureFlagOff_returnsFalse() {
-    setOverride(OVERRIDE_UNSET.setting)
-
-    // For unset overrides, follow flag
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(
-      FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-      FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-      FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagEnabled_overrideOn_featureFlagOn_returnsTrue() {
-    setOverride(OVERRIDE_ON.setting)
-
-    // When toggle override matches its default state (dw flag), don't override flags
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagEnabled_overrideOn_featureFlagOff_returnFalse() {
-    setOverride(OVERRIDE_ON.setting)
-
-    // When toggle override matches its default state (dw flag), don't override flags
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(
-      FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
-      FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
-      FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagEnabled_overrideOff_featureFlagOn_returnsFalse() {
-    setOverride(OVERRIDE_OFF.setting)
-
-    // Follow override if they exist, and is not equal to default toggle state (dw flag)
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagEnabled_overrideOff_featureFlagOff_returnsFalse() {
-    setOverride(OVERRIDE_OFF.setting)
-
-    // Follow override if they exist, and is not equal to default toggle state (dw flag)
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(
-      FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_dwFlagDisabled_overrideUnset_featureFlagOn_returnsTrue() {
-    setOverride(OVERRIDE_UNSET.setting)
-
-    // For unset overrides, follow flag
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(
-      FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagDisabled_overrideUnset_featureFlagOff_returnsFalse() {
-    setOverride(OVERRIDE_UNSET.setting)
-
-    // For unset overrides, follow flag
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  @EnableFlags(
-      FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_dwFlagDisabled_overrideOn_featureFlagOn_returnsTrue() {
-    setOverride(OVERRIDE_ON.setting)
-
-    // Follow override if they exist, and is not equal to default toggle state (dw flag)
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(
-      FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagDisabled_overrideOn_featureFlagOff_returnTrue() {
-    setOverride(OVERRIDE_ON.setting)
-
-    // Follow override if they exist, and is not equal to default toggle state (dw flag)
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(
-      FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
-  fun isEnabled_dwFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
-    setOverride(OVERRIDE_OFF.setting)
-
-    // When toggle override matches its default state (dw flag), don't override flags
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue()
-  }
-
-  @Test
-  @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
-  @DisableFlags(
-      FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
-  fun isEnabled_dwFlagDisabled_overrideOff_featureFlagOff_returnsFalse() {
-    setOverride(OVERRIDE_OFF.setting)
-
-    // When toggle override matches its default state (dw flag), don't override flags
-    assertThat(WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse()
-  }
-
-  @Test
-  fun convertToToggleOverrideWithFallback_validInt_returnsToggleOverride() {
-    assertThat(convertToToggleOverrideWithFallback(0, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_OFF)
-    assertThat(convertToToggleOverrideWithFallback(1, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_ON)
-    assertThat(convertToToggleOverrideWithFallback(-1, OVERRIDE_ON)).isEqualTo(OVERRIDE_UNSET)
-  }
-
-  @Test
-  fun convertToToggleOverrideWithFallback_invalidInt_returnsFallback() {
-    assertThat(convertToToggleOverrideWithFallback(2, OVERRIDE_ON)).isEqualTo(OVERRIDE_ON)
-    assertThat(convertToToggleOverrideWithFallback(-2, OVERRIDE_UNSET)).isEqualTo(OVERRIDE_UNSET)
-  }
-
-  private fun setOverride(setting: Int?) {
-    val contentResolver = mContext.contentResolver
-    val key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES
-    if (setting == null) {
-      Settings.Global.putString(contentResolver, key, null)
-    } else {
-      Settings.Global.putInt(contentResolver, key, setting)
-    }
-  }
-
-  private fun resetToggleOverrideCache() {
-    val cachedToggleOverride =
-      DesktopModeFlags::class.java.getDeclaredField("cachedToggleOverride")
-    cachedToggleOverride.isAccessible = true
-    cachedToggleOverride.set(null, null)
-  }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS
deleted file mode 100644
index 2fabd4a..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/OWNERS
new file mode 100644
index 0000000..9d926b2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 928697
+# includes OWNERS from parent directories
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index a6c16c4..67eda8b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -74,6 +74,7 @@
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.DefaultMixedHandler;
+import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.HomeTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 
@@ -429,7 +430,8 @@
         ShellInit shellInit = new ShellInit(mMainExecutor);
         final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
                 mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor,
-                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
+                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+                mock(FocusTransitionObserver.class));
         shellInit.init();
         return t;
     }
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 0c18229..e540322 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
@@ -25,11 +25,14 @@
 import static android.window.TransitionInfo.FLAG_SYNC;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.animation.Animator;
+import android.animation.ValueAnimator;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 import android.os.Binder;
@@ -37,6 +40,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.view.SurfaceControl;
+import android.view.animation.AlphaAnimation;
 import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 
@@ -56,6 +60,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+
 /**
  * Tests for the default animation handler that is used if no other special-purpose handler picks
  * up an animation request.
@@ -187,6 +193,34 @@
     }
 
     @Test
+    public void testBuildSurfaceAnimation() {
+        final ArrayList<Animator> animators = new ArrayList<>();
+        final AlphaAnimation animation = new AlphaAnimation(0, 1);
+        final long durationMs = 500;
+        animation.setDuration(durationMs);
+        final long[] lastCurrentPlayTime = new long[1];
+        final int[] finishCount = new int[1];
+        final Runnable finishCallback = () -> finishCount[0]++;
+        DefaultSurfaceAnimator.buildSurfaceAnimation(animators, animation, finishCallback,
+                mTransactionPool, mMainExecutor,
+                new DefaultSurfaceAnimator.AnimationAdapter(mock(SurfaceControl.class)) {
+                    @Override
+                    void applyTransformation(ValueAnimator animator, long currentPlayTime) {
+                        lastCurrentPlayTime[0] = currentPlayTime;
+                    }
+                });
+        final ValueAnimator animator = (ValueAnimator) animators.get(0);
+        mAnimExecutor.execute(() -> {
+            animator.start();
+            animator.end();
+        });
+        flushHandlers();
+        assertEquals(durationMs, lastCurrentPlayTime[0]);
+        assertEquals(1f, animator.getAnimatedFraction(), 0f /* delta */);
+        assertEquals(1, finishCount[0]);
+    }
+
+    @Test
     public void startAnimation_freeformOpenChange_doesntReparentTask() {
         final TransitionInfo.Change openChange = new ChangeBuilder(TRANSIT_OPEN)
                 .setTask(createTaskInfo(
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
new file mode 100644
index 0000000..d37b4cf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.transition;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+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;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+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.common.DisplayController;
+import com.android.wm.shell.shared.IFocusTransitionListener;
+import com.android.wm.shell.shared.TransactionPool;
+import com.android.wm.shell.sysui.ShellController;
+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 java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for the focus transition observer.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
+public class FocusTransitionObserverTest extends ShellTestCase {
+
+    static final int SECONDARY_DISPLAY_ID = 1;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    private IFocusTransitionListener mListener;
+    private Transitions mTransition;
+    private FocusTransitionObserver mFocusTransitionObserver;
+
+    @Before
+    public void setUp() {
+        mListener = mock(IFocusTransitionListener.class);
+        when(mListener.asBinder()).thenReturn(mock(IBinder.class));
+
+        mFocusTransitionObserver = new FocusTransitionObserver();
+        mTransition =
+                new Transitions(InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                        mock(ShellInit.class), mock(ShellController.class),
+                        mock(ShellTaskOrganizer.class), mock(TransactionPool.class),
+                        mock(DisplayController.class), new TestShellExecutor(),
+                        new Handler(Looper.getMainLooper()), new TestShellExecutor(),
+                        mock(HomeTransitionObserver.class),
+                mFocusTransitionObserver);
+        mFocusTransitionObserver.setRemoteFocusTransitionListener(mTransition, mListener);
+    }
+
+    @Test
+    public void testTransitionWithMovedToFrontFlagChangesDisplayFocus() throws RemoteException {
+        final IBinder binder = mock(IBinder.class);
+        final SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+
+        // Open a task on the default display, which doesn't change display focus because the
+        // default display already has it.
+        TransitionInfo info = mock(TransitionInfo.class);
+        final List<TransitionInfo.Change> changes = new ArrayList<>();
+        setupChange(changes, 123 /* taskId */, TRANSIT_OPEN, DEFAULT_DISPLAY,
+                true /* focused */);
+        when(info.getChanges()).thenReturn(changes);
+        mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx);
+        verify(mListener, never()).onFocusedDisplayChanged(SECONDARY_DISPLAY_ID);
+        clearInvocations(mListener);
+
+        // Open a new task on the secondary display and verify display focus changes to the display.
+        changes.clear();
+        setupChange(changes, 456 /* taskId */, TRANSIT_OPEN, SECONDARY_DISPLAY_ID,
+                true /* focused */);
+        when(info.getChanges()).thenReturn(changes);
+        mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx);
+        verify(mListener, times(1)).onFocusedDisplayChanged(SECONDARY_DISPLAY_ID);
+        clearInvocations(mListener);
+
+        // Open the first task to front and verify display focus goes back to the default display.
+        changes.clear();
+        setupChange(changes, 123 /* taskId */, TRANSIT_TO_FRONT, DEFAULT_DISPLAY,
+                true /* focused */);
+        when(info.getChanges()).thenReturn(changes);
+        mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx);
+        verify(mListener, times(1)).onFocusedDisplayChanged(DEFAULT_DISPLAY);
+        clearInvocations(mListener);
+
+        // Open another task on the default display and verify no display focus switch as it's
+        // already on the default display.
+        changes.clear();
+        setupChange(changes, 789 /* taskId */, TRANSIT_OPEN, DEFAULT_DISPLAY,
+                true /* focused */);
+        when(info.getChanges()).thenReturn(changes);
+        mFocusTransitionObserver.onTransitionReady(binder, info, tx, tx);
+        verify(mListener, never()).onFocusedDisplayChanged(DEFAULT_DISPLAY);
+    }
+
+    private void setupChange(List<TransitionInfo.Change> changes, int taskId,
+            @TransitionMode int mode, int displayId, boolean focused) {
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        RunningTaskInfo taskInfo = mock(RunningTaskInfo.class);
+        taskInfo.taskId = taskId;
+        taskInfo.isFocused = focused;
+        when(change.hasFlags(FLAG_MOVED_TO_TOP)).thenReturn(focused);
+        taskInfo.displayId = displayId;
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(change.getMode()).thenReturn(mode);
+        changes.add(change);
+    }
+}
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 8f49de0..8dfdfb4 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
@@ -100,7 +100,8 @@
         mHomeTransitionObserver = new HomeTransitionObserver(mContext, mMainExecutor);
         mTransition = new Transitions(mContext, mock(ShellInit.class), mock(ShellController.class),
                 mOrganizer, mTransactionPool, mDisplayController, mMainExecutor,
-                mMainHandler, mAnimExecutor, mHomeTransitionObserver);
+                mMainHandler, mAnimExecutor, mHomeTransitionObserver,
+                mock(FocusTransitionObserver.class));
         mHomeTransitionObserver.setHomeTransitionListener(mTransition, mListener);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/OWNERS
new file mode 100644
index 0000000..a24088a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316275
+# includes OWNERS from parent directories
\ No newline at end of file
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 aea14b9..6cde056 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
@@ -158,7 +158,8 @@
         ShellInit shellInit = mock(ShellInit.class);
         final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
                 mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
-                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
+                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+                mock(FocusTransitionObserver.class));
         // One from Transitions, one from RootTaskDisplayAreaOrganizer
         verify(shellInit).addInitCallback(any(), eq(t));
         verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class));
@@ -170,7 +171,8 @@
         ShellController shellController = mock(ShellController.class);
         final Transitions t = new Transitions(mContext, shellInit, shellController,
                 mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
-                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
+                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+                mock(FocusTransitionObserver.class));
         shellInit.init();
         verify(shellController, times(1)).addExternalInterface(
                 eq(ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS), any(), any());
@@ -1238,7 +1240,8 @@
         final Transitions transitions =
                 new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer,
                         mTransactionPool, createTestDisplayController(), mMainExecutor,
-                        mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
+                        mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+                        mock(FocusTransitionObserver.class));
         final RecentsTransitionHandler recentsHandler =
                 new RecentsTransitionHandler(shellInit, mock(ShellTaskOrganizer.class), transitions,
                         mock(RecentTasksController.class), mock(HomeTransitionObserver.class));
@@ -1780,7 +1783,8 @@
         ShellInit shellInit = new ShellInit(mMainExecutor);
         final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
                 mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
-                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
+                mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+                mock(FocusTransitionObserver.class));
         shellInit.init();
         return t;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/OWNERS
new file mode 100644
index 0000000..f5ba614
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1267635
+# includes OWNERS from parent directories
\ No newline at end of file
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 ee2a41c..3051714 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
@@ -100,7 +100,6 @@
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier
 import java.util.Optional
 import java.util.function.Consumer
 import java.util.function.Supplier
@@ -186,7 +185,6 @@
             DesktopModeWindowDecorViewModel.TaskPositionerFactory
     @Mock private lateinit var mockTaskPositioner: TaskPositioner
     @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
-    @Mock private lateinit var mockWindowDecorViewHostSupplier: WindowDecorViewHostSupplier<*>
     private lateinit var spyContext: TestableContext
 
     private val transactionFactory = Supplier<SurfaceControl.Transaction> {
@@ -236,7 +234,6 @@
                 mockGenericLinksParser,
                 mockAssistContentRequester,
                 mockMultiInstanceHelper,
-                mockWindowDecorViewHostSupplier,
                 mockDesktopModeWindowDecorFactory,
                 mockInputMonitorFactory,
                 transactionFactory,
@@ -1217,7 +1214,7 @@
         whenever(
             mockDesktopModeWindowDecorFactory.create(
                 any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
-                any(), any(), any(), any(), any(), any(), any())
+                any(), any(), any(), any(), any(), any())
         ).thenReturn(decoration)
         decoration.mTaskInfo = task
         whenever(decoration.isFocused).thenReturn(task.isFocused)
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 a1867f3..f007115 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
@@ -47,6 +47,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.VerificationKt.times;
 
 import android.app.ActivityManager;
 import android.app.assist.AssistContent;
@@ -106,8 +107,6 @@
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
 import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
@@ -178,10 +177,6 @@
     @Mock
     private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
     @Mock
-    private WindowDecorViewHostSupplier mMockWindowDecorViewHostSupplier;
-    @Mock
-    private WindowDecorViewHost mMockWindowDecorViewHost;
-    @Mock
     private TypedArray mMockRoundedCornersRadiusArray;
     @Mock
     private TestTouchEventListener mMockTouchEventListener;
@@ -257,9 +252,6 @@
                 anyBoolean(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt()))
                 .thenReturn(mMockHandleMenu);
         when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
-        when(mMockWindowDecorViewHostSupplier.acquire(any(), eq(defaultDisplay)))
-                .thenReturn(mMockWindowDecorViewHost);
-        when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
         when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(),
                 any())).thenReturn(mMockAppHeaderViewHolder);
     }
@@ -533,42 +525,6 @@
     }
 
     @Test
-    public void updateRelayoutParams_handle_requestsAsyncViewHostRendering() {
-        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
-        // Make the task fullscreen so that its decoration is an App Handle.
-        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        final RelayoutParams relayoutParams = new RelayoutParams();
-
-        DesktopModeWindowDecoration.updateRelayoutParams(
-                relayoutParams,
-                mTestableContext,
-                taskInfo,
-                /* applyStartTransactionOnDraw= */ true,
-                /* shouldSetTaskPositionAndCrop */ false);
-
-        // App Handles don't need to be rendered in sync with the task animation, per UX.
-        assertThat(relayoutParams.mAsyncViewHost).isTrue();
-    }
-
-    @Test
-    public void updateRelayoutParams_header_requestsSyncViewHostRendering() {
-        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
-        // Make the task freeform so that its decoration is an App Header.
-        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        final RelayoutParams relayoutParams = new RelayoutParams();
-
-        DesktopModeWindowDecoration.updateRelayoutParams(
-                relayoutParams,
-                mTestableContext,
-                taskInfo,
-                /* applyStartTransactionOnDraw= */ true,
-                /* shouldSetTaskPositionAndCrop */ false);
-
-        // App Headers must be rendered in sync with the task animation, so it cannot be delayed.
-        assertThat(relayoutParams.mAsyncViewHost).isFalse();
-    }
-
-    @Test
     public void relayout_fullscreenTask_appliesTransactionImmediately() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -592,7 +548,78 @@
         spyWindowDecor.relayout(taskInfo);
 
         verify(mMockTransaction, never()).apply();
-        verify(mMockWindowDecorViewHost).updateView(any(), any(), any(), eq(mMockTransaction));
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
+    }
+
+    @Test
+    public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        spyWindowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
+    }
+
+    @Test
+    public void relayout_fullscreenTask_postsViewHostCreation() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+        spyWindowDecor.relayout(taskInfo);
+
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+        runnableArgument.getValue().run();
+        verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+    }
+
+    @Test
+    @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error")
+    public void relayout_freeformTask_createsViewHostImmediately() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
+        taskInfo.isResizeable = false;
+
+        spyWindowDecor.relayout(taskInfo);
+
+        verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+        verify(mMockHandler, never()).post(any());
+    }
+
+    @Test
+    public void relayout_removesExistingHandlerCallback() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+        spyWindowDecor.relayout(taskInfo);
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+
+        spyWindowDecor.relayout(taskInfo);
+
+        verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
+    }
+
+    @Test
+    public void close_removesExistingHandlerCallback() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+        spyWindowDecor.relayout(taskInfo);
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
+
+        spyWindowDecor.close();
+
+        verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
     }
 
     @Test
@@ -623,7 +650,7 @@
                 .postDelayed(mCloseMaxMenuRunnable.capture(), eq(CLOSE_MAXIMIZE_MENU_DELAY_MS));
 
         mCloseMaxMenuRunnable.getValue().run();
-        verify(menu).close();
+        verify(menu).close(any());
         assertFalse(decoration.isMaximizeMenuActive());
     }
 
@@ -642,7 +669,7 @@
                 .postDelayed(mCloseMaxMenuRunnable.capture(), eq(CLOSE_MAXIMIZE_MENU_DELAY_MS));
 
         mCloseMaxMenuRunnable.getValue().run();
-        verify(menu).close();
+        verify(menu).close(any());
         assertFalse(decoration.isMaximizeMenuActive());
     }
 
@@ -1065,7 +1092,7 @@
                 mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
                 mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
                 new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
-                mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
+                maximizeMenuFactory, mMockHandleMenuFactory,
                 mMockMultiInstanceHelper, mMockCaptionHandleRepository);
         windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
                 mMockTouchEventListener, mMockTouchEventListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
index 56224b4..7f7211d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -21,6 +21,7 @@
 import android.view.MotionEvent
 import android.view.InputDevice
 import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
 import org.junit.After
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -34,6 +35,7 @@
 import org.mockito.Mockito.argThat
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.times
 
 /**
  * Tests for [DragDetector].
@@ -43,22 +45,17 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-class DragDetectorTest {
+class DragDetectorTest : ShellTestCase() {
     private val motionEvents = mutableListOf<MotionEvent>()
 
     @Mock
     private lateinit var eventHandler: DragDetector.MotionEventHandler
 
-    private lateinit var dragDetector: DragDetector
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
         `when`(eventHandler.handleMotionEvent(any(), any())).thenReturn(true)
-
-        dragDetector = DragDetector(eventHandler)
-        dragDetector.setTouchSlop(SLOP)
     }
 
     @After
@@ -71,6 +68,7 @@
 
     @Test
     fun testNoMove_passesDownAndUp() {
+        val dragDetector = createDragDetector()
         assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
         verify(eventHandler).handleMotionEvent(any(), argThat {
             return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
@@ -86,6 +84,7 @@
 
     @Test
     fun testNoMove_mouse_passesDownAndUp() {
+        val dragDetector = createDragDetector()
         assertTrue(dragDetector.onMotionEvent(
             createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
         verify(eventHandler).handleMotionEvent(any(), argThat {
@@ -103,6 +102,7 @@
 
     @Test
     fun testMoveInSlop_touch_passesDownAndUp() {
+        val dragDetector = createDragDetector()
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             return@argThat it.action == MotionEvent.ACTION_DOWN
         })).thenReturn(false)
@@ -129,6 +129,7 @@
 
     @Test
     fun testMoveInSlop_mouse_passesDownMoveAndUp() {
+        val dragDetector = createDragDetector()
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             it.action == MotionEvent.ACTION_DOWN
         })).thenReturn(false)
@@ -158,6 +159,7 @@
 
     @Test
     fun testMoveBeyondSlop_passesDownMoveAndUp() {
+        val dragDetector = createDragDetector()
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             it.action == MotionEvent.ACTION_DOWN
         })).thenReturn(false)
@@ -184,6 +186,7 @@
 
     @Test
     fun testDownMoveDown_shouldIgnoreTheSecondDownMotion() {
+        val dragDetector = createDragDetector()
         assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
         verify(eventHandler).handleMotionEvent(any(), argThat {
             return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
@@ -206,6 +209,7 @@
 
     @Test
     fun testDownMouseMoveDownTouch_shouldIgnoreTheTouchDownMotion() {
+        val dragDetector = createDragDetector()
         assertTrue(dragDetector.onMotionEvent(
             createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
         verify(eventHandler).handleMotionEvent(any(), argThat {
@@ -230,6 +234,7 @@
 
     @Test
     fun testPassesHoverEnter() {
+        val dragDetector = createDragDetector()
         `when`(eventHandler.handleMotionEvent(any(), argThat {
             it.action == MotionEvent.ACTION_HOVER_ENTER
         })).thenReturn(false)
@@ -242,6 +247,7 @@
 
     @Test
     fun testPassesHoverMove() {
+        val dragDetector = createDragDetector()
         assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
         verify(eventHandler).handleMotionEvent(any(), argThat {
             return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
@@ -250,21 +256,240 @@
 
     @Test
     fun testPassesHoverExit() {
+        val dragDetector = createDragDetector()
         assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
         verify(eventHandler).handleMotionEvent(any(), argThat {
             return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
         })
     }
 
-    private fun createMotionEvent(action: Int, x: Float = X, y: Float = Y, isTouch: Boolean = true):
-            MotionEvent {
-        val time = SystemClock.uptimeMillis()
-        val ev = MotionEvent.obtain(time, time, action, x, y, 0)
+    @Test
+    fun testHoldToDrag_holdsWithMovementWithinSlop_passesDragMoveEvents() {
+        val dragDetector = createDragDetector(holdToDragMinDurationMs = 100, slop = 20)
+        val downTime = SystemClock.uptimeMillis()
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_DOWN,
+            x = 500f,
+            y = 10f,
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime
+        ))
+
+        // Couple of movements within the slop, still counting as "holding"
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 10f, // within slop
+            y = 10f + 10f, // within slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 30
+        ))
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f - 10f, // within slop
+            y = 10f - 5f, // within slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 70
+        ))
+        // Now go beyond slop, but after the required holding period.
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 50f, // beyond slop
+            y = 10f + 50f, // beyond slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 101 // after hold period
+        ))
+
+        // Had a valid hold, so there should be 1 "move".
+        verify(eventHandler, times(1))
+            .handleMotionEvent(any(), argThat { ev -> ev.action == MotionEvent.ACTION_MOVE })
+    }
+
+    @Test
+    fun testHoldToDrag_holdsWithoutAnyMovement_passesMoveEvents() {
+        val dragDetector = createDragDetector(holdToDragMinDurationMs = 100, slop = 20)
+        val downTime = SystemClock.uptimeMillis()
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_DOWN,
+            x = 500f,
+            y = 10f,
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime
+        ))
+
+        // First |move| is already beyond slop and after holding period.
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 50f, // beyond slop
+            y = 10f + 50f, // beyond slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 101 // after hold period
+        ))
+
+        // Considered a valid hold, so there should be 1 "move".
+        verify(eventHandler, times(1))
+            .handleMotionEvent(any(), argThat { ev -> ev.action == MotionEvent.ACTION_MOVE })
+    }
+
+    @Test
+    fun testHoldToDrag_returnsWithinSlopAfterHoldPeriod_passesDragMoveEvents() {
+        val dragDetector = createDragDetector(holdToDragMinDurationMs = 100, slop = 20)
+        val downTime = SystemClock.uptimeMillis()
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_DOWN,
+            x = 500f,
+            y = 10f,
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime
+        ))
+        // Go beyond slop after the required holding period.
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 50f, // beyond slop
+            y = 10f + 50f, // beyond slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 101 // after hold period
+        ))
+
+        // Return to original coordinates after holding period.
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f, // within slop
+            y = 10f, // within slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 102 // after hold period
+        ))
+
+        // Both |moves| should be passed, even the one in the slop region since it was after the
+        // holding period. (e.g. after you drag the handle you may return to its original position).
+        verify(eventHandler, times(2))
+            .handleMotionEvent(any(), argThat { ev -> ev.action == MotionEvent.ACTION_MOVE })
+    }
+
+    @Test
+    fun testHoldToDrag_straysDuringHoldPeriod_skipsMoveEvents() {
+        val dragDetector = createDragDetector(holdToDragMinDurationMs = 100, slop = 20)
+        val downTime = SystemClock.uptimeMillis()
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_DOWN,
+            x = 500f,
+            y = 10f,
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime
+        ))
+
+        // Go beyond slop before the required holding period.
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 50f, // beyond slop
+            y = 10f + 50f, // beyond slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 30 // during hold period
+        ))
+
+        // The |move| was too quick and did not held, do not pass it to the handler.
+        verify(eventHandler, never())
+            .handleMotionEvent(any(), argThat { ev -> ev.action == MotionEvent.ACTION_MOVE })
+    }
+
+    @Test
+    fun testHoldToDrag_straysDuringHoldPeriodAndReturnsWithinSlop_skipsMoveEvents() {
+        val dragDetector = createDragDetector(holdToDragMinDurationMs = 100, slop = 20)
+        val downTime = SystemClock.uptimeMillis()
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_DOWN,
+            x = 500f,
+            y = 10f,
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime
+        ))
+        // Go beyond slop before the required holding period.
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 50f, // beyond slop
+            y = 10f + 50f, // beyond slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 30 // during hold period
+        ))
+
+        // Return to slop area during holding period.
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 10f, // within slop
+            y = 10f + 10f, // within slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 50 // during hold period
+        ))
+
+        // The first |move| invalidates the drag even if you return within the hold period, so the
+        // |move| should not be passed to the handler.
+        verify(eventHandler, never())
+            .handleMotionEvent(any(), argThat { ev -> ev.action == MotionEvent.ACTION_MOVE })
+    }
+
+    @Test
+    fun testHoldToDrag_noHoldRequired_passesMoveEvents() {
+        val dragDetector = createDragDetector(holdToDragMinDurationMs = 0, slop = 20)
+        val downTime = SystemClock.uptimeMillis()
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_DOWN,
+            x = 500f,
+            y = 10f,
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime
+        ))
+
+        dragDetector.onMotionEvent(createMotionEvent(
+            action = MotionEvent.ACTION_MOVE,
+            x = 500f + 50f, // beyond slop
+            y = 10f + 50f, // beyond slop
+            isTouch = true,
+            downTime = downTime,
+            eventTime = downTime + 1
+        ))
+
+        // The |move| should be passed to the handler as no hold period was needed.
+        verify(eventHandler, times(1))
+            .handleMotionEvent(any(), argThat { ev -> ev.action == MotionEvent.ACTION_MOVE })
+    }
+
+    private fun createMotionEvent(
+        action: Int,
+        x: Float = X,
+        y: Float = Y,
+        isTouch: Boolean = true,
+        downTime: Long = SystemClock.uptimeMillis(),
+        eventTime: Long = SystemClock.uptimeMillis()
+    ): MotionEvent {
+        val ev = MotionEvent.obtain(downTime, eventTime, action, x, y, 0)
         ev.source = if (isTouch) InputDevice.SOURCE_TOUCHSCREEN else InputDevice.SOURCE_MOUSE
         motionEvents.add(ev)
         return ev
     }
 
+    private fun createDragDetector(
+        holdToDragMinDurationMs: Long = 0,
+        slop: Int = SLOP
+    ) = DragDetector(
+        eventHandler,
+        holdToDragMinDurationMs,
+        slop
+    )
+
     companion object {
         private const val SLOP = 10
         private const val X = 123f
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 1691f07..57469bf 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
@@ -128,7 +128,7 @@
     @Test
     public void testRegionUnionContainsEdges() {
         Region region = new Region();
-        GEOMETRY.union(mContext, region);
+        GEOMETRY.union(region);
         assertThat(region.isComplex()).isTrue();
         // Region excludes task area. Note that coordinates start from top left.
         assertThat(region.contains(TASK_SIZE.getWidth() / 2, TASK_SIZE.getHeight() / 2)).isFalse();
@@ -168,7 +168,7 @@
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testRegionUnion_edgeDragResizeEnabled_containsLargeCorners() {
         Region region = new Region();
-        GEOMETRY.union(mContext, region);
+        GEOMETRY.union(region);
         final int cornerRadius = LARGE_CORNER_SIZE / 2;
 
         new TestPoints(mContext, TASK_SIZE, cornerRadius).validateRegion(region);
@@ -182,7 +182,7 @@
     @DisableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testRegionUnion_edgeDragResizeDisabled_containsFineCorners() {
         Region region = new Region();
-        GEOMETRY.union(mContext, region);
+        GEOMETRY.union(region);
         final int cornerRadius = FINE_CORNER_SIZE / 2;
 
         new TestPoints(mContext, TASK_SIZE, cornerRadius).validateRegion(region);
@@ -215,7 +215,7 @@
                 CTRL_TYPE_BOTTOM);
 
         for (int i = 0; i < points.size(); i++) {
-            assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     points.get(i).x, points.get(i).y)).isEqualTo(
                     isEdgeResizePermitted ? expectedCtrlType.get(i) : CTRL_TYPE_UNDEFINED);
         }
@@ -366,17 +366,17 @@
         public void validateCtrlTypeForInnerPoints(@NonNull DragResizeWindowGeometry geometry,
                 boolean isTouchscreen, boolean isEdgeResizePermitted,
                 boolean expectedWithinGeometry) {
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mTopLeftPoint.x, mTopLeftPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mTopRightPoint.x, mTopRightPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mBottomLeftPoint.x, mBottomLeftPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mBottomRightPoint.x, mBottomRightPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
@@ -389,17 +389,17 @@
         public void validateCtrlTypeForOutsidePoints(@NonNull DragResizeWindowGeometry geometry,
                 boolean isTouchscreen, boolean isEdgeResizePermitted,
                 boolean expectedWithinGeometry) {
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mTopLeftPointOutside.x, mTopLeftPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mTopRightPointOutside.x, mTopRightPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mBottomLeftPointOutside.x, mBottomLeftPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
                     mBottomRightPointOutside.x, mBottomRightPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/OWNERS
new file mode 100644
index 0000000..553540c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 929241
+# includes OWNERS from parent directories
\ No newline at end of file
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 7252b32..2e117ac 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
@@ -85,8 +85,6 @@
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.tests.R;
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHost;
-import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -130,10 +128,6 @@
     @Mock
     private SurfaceControlViewHost mMockSurfaceControlViewHost;
     @Mock
-    private WindowDecorViewHostSupplier mMockWindowDecorViewHostSupplier;
-    @Mock
-    private WindowDecorViewHost mMockWindowDecorViewHost;
-    @Mock
     private AttachedSurfaceControl mMockRootSurfaceControl;
     @Mock
     private TestView mMockView;
@@ -173,9 +167,6 @@
         when(mMockSurfaceControlViewHost.getRootSurfaceControl())
                 .thenReturn(mMockRootSurfaceControl);
         when(mMockView.findViewById(anyInt())).thenReturn(mMockView);
-        when(mMockWindowDecorViewHostSupplier.acquire(any(), any()))
-                .thenReturn(mMockWindowDecorViewHost);
-        when(mMockWindowDecorViewHost.getSurfaceControl()).thenReturn(mock(SurfaceControl.class));
 
         // Add status bar inset so that WindowDecoration does not think task is in immersive mode
         mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true);
@@ -239,6 +230,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
                 .setDisplayId(Display.DEFAULT_DISPLAY)
@@ -259,18 +254,18 @@
         verify(mMockSurfaceControlStartT).setTrustedOverlay(decorContainerSurface, true);
         verify(mMockSurfaceControlStartT).setWindowCrop(decorContainerSurface, 300, 100);
 
-        final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
-        verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setContainerLayer();
         verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
         verify(mMockSurfaceControlStartT).show(captionContainerSurface);
 
-        verify(mMockWindowDecorViewHost).updateView(
-                same(mMockView),
-                argThat(lp -> lp.height == 64
-                        && lp.width == 300
-                        && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0),
-                eq(taskInfo.configuration),
-                eq(null) /* onDrawTransaction */);
+        verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
+
+        verify(mMockSurfaceControlViewHost)
+                .setView(same(mMockView),
+                        argThat(lp -> lp.height == 64
+                                && lp.width == 300
+                                && (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
         verify(mMockView).setTaskFocusState(true);
         verify(mMockWindowContainerTransaction).addInsetsSource(
                 eq(taskInfo.token),
@@ -301,6 +296,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -323,7 +322,7 @@
 
         windowDecor.relayout(taskInfo);
 
-        verify(mMockWindowDecorViewHost, never()).release(any());
+        verify(mMockSurfaceControlViewHost, never()).release();
         verify(t, never()).apply();
         verify(mMockWindowContainerTransaction, never())
                 .removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
@@ -333,8 +332,9 @@
         taskInfo.isVisible = false;
         windowDecor.relayout(taskInfo);
 
-        final InOrder releaseOrder = inOrder(t2, mMockWindowDecorViewHostSupplier);
-        releaseOrder.verify(mMockWindowDecorViewHostSupplier).release(mMockWindowDecorViewHost, t2);
+        final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost);
+        releaseOrder.verify(mMockSurfaceControlViewHost).release();
+        releaseOrder.verify(t2).remove(captionContainerSurface);
         releaseOrder.verify(t2).remove(decorContainerSurface);
         releaseOrder.verify(t2).apply();
         // Expect to remove two insets sources, the caption insets and the mandatory gesture insets.
@@ -382,8 +382,8 @@
         verify(mMockDisplayController).removeDisplayWindowListener(same(listener));
 
         assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView);
-        verify(mMockWindowDecorViewHostSupplier).acquire(any(), eq(mockDisplay));
-        verify(mMockWindowDecorViewHost).updateView(same(mMockView), any(), any(), any());
+        verify(mMockSurfaceControlViewHostFactory).create(any(), eq(mockDisplay), any());
+        verify(mMockSurfaceControlViewHost).setView(same(mMockView), any());
     }
 
     @Test
@@ -396,6 +396,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -432,7 +436,8 @@
                 windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId);
         verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height);
         verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
-        verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
+        verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
+                .create(any(), eq(defaultDisplay), any());
     }
 
     @Test
@@ -445,6 +450,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -464,8 +473,8 @@
 
         windowDecor.relayout(taskInfo);
 
-        final SurfaceControl captionContainerSurface = mMockWindowDecorViewHost.getSurfaceControl();
-        verify(mMockSurfaceControlStartT).reparent(captionContainerSurface, decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+        verify(captionContainerSurfaceBuilder).setContainerLayer();
         // Width of the captionContainerSurface should match the width of TASK_BOUNDS
         verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
         verify(mMockSurfaceControlStartT).show(captionContainerSurface);
@@ -481,6 +490,10 @@
         final SurfaceControl.Builder decorContainerSurfaceBuilder =
                 createMockSurfaceControlBuilder(decorContainerSurface);
         mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+        final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+        final SurfaceControl.Builder captionContainerSurfaceBuilder =
+                createMockSurfaceControlBuilder(captionContainerSurface);
+        mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
 
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         mMockSurfaceControlTransactions.add(t);
@@ -498,11 +511,9 @@
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        mRelayoutParams.mApplyStartTransactionOnDraw = true;
-        windowDecor.relayout(taskInfo);
+        windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */);
 
-        verify(mMockWindowDecorViewHost).updateView(any(), any(), any(),
-                eq(mMockSurfaceControlStartT));
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
     }
 
     @Test
@@ -856,52 +867,37 @@
     }
 
     @Test
-    public void relayout_applyTransactionOnDrawIsTrue_updatesViewWithDrawTransaction() {
+    public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() {
         final TestWindowDecoration windowDecor = createWindowDecoration(
-                new TestRunningTaskInfoBuilder()
-                        .setVisible(true)
-                        .setWindowingMode(WINDOWING_MODE_FREEFORM)
-                        .build());
+                new TestRunningTaskInfoBuilder().build());
         mRelayoutParams.mApplyStartTransactionOnDraw = true;
         mRelayoutResult.mRootView = mMockView;
 
-        windowDecor.relayout(windowDecor.mTaskInfo);
+        windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult);
 
-        verify(mMockWindowDecorViewHost)
-                .updateView(eq(mRelayoutResult.mRootView), any(),
-                        eq(windowDecor.mTaskInfo.configuration), eq(mMockSurfaceControlStartT));
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
     }
 
     @Test
-    public void relayout_applyTransactionOnDrawIsTrue_asyncViewHostRendering_throwsException() {
+    public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() {
         final TestWindowDecoration windowDecor = createWindowDecoration(
-                new TestRunningTaskInfoBuilder()
-                        .setVisible(true)
-                        .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                        .build());
+                new TestRunningTaskInfoBuilder().build());
         mRelayoutParams.mApplyStartTransactionOnDraw = true;
-        mRelayoutParams.mAsyncViewHost = true;
         mRelayoutResult.mRootView = mMockView;
 
         assertThrows(IllegalArgumentException.class,
-                () -> windowDecor.relayout(windowDecor.mTaskInfo));
+                () -> windowDecor.updateViewHost(
+                        mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult));
     }
 
     @Test
-    public void relayout_asyncViewHostRendering() {
+    public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() {
         final TestWindowDecoration windowDecor = createWindowDecoration(
-                new TestRunningTaskInfoBuilder()
-                        .setVisible(true)
-                        .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
-                        .build());
-        mRelayoutParams.mAsyncViewHost = true;
+                new TestRunningTaskInfoBuilder().build());
+        mRelayoutParams.mApplyStartTransactionOnDraw = false;
         mRelayoutResult.mRootView = mMockView;
 
-        windowDecor.relayout(windowDecor.mTaskInfo);
-
-        verify(mMockWindowDecorViewHost)
-                .updateViewAsync(eq(mRelayoutResult.mRootView), any(),
-                        eq(windowDecor.mTaskInfo.configuration));
+        windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult);
     }
 
     @Test
@@ -1001,8 +997,7 @@
                 new MockObjectSupplier<>(mMockSurfaceControlTransactions,
                         () -> mock(SurfaceControl.Transaction.class)),
                 () -> mMockWindowContainerTransaction, () -> mMockTaskSurface,
-                mMockSurfaceControlViewHostFactory,
-                mMockWindowDecorViewHostSupplier);
+                mMockSurfaceControlViewHostFactory);
     }
 
     private class MockObjectSupplier<T> implements Supplier<T> {
@@ -1042,20 +1037,16 @@
                 Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
                 Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
                 Supplier<SurfaceControl> surfaceControlSupplier,
-                SurfaceControlViewHostFactory surfaceControlViewHostFactory,
-                @NonNull WindowDecorViewHostSupplier windowDecorViewHostSupplier) {
+                SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
             super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
                     surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                     windowContainerTransactionSupplier, surfaceControlSupplier,
-                    surfaceControlViewHostFactory, windowDecorViewHostSupplier);
+                    surfaceControlViewHostFactory);
         }
 
         @Override
         void relayout(ActivityManager.RunningTaskInfo taskInfo) {
-            mRelayoutParams.mRunningTaskInfo = taskInfo;
-            mRelayoutParams.mLayoutResId = R.layout.caption_layout;
-            relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
-                    mMockWindowContainerTransaction, mMockView, mRelayoutResult);
+            relayout(taskInfo, false /* applyStartTransactionOnDraw */);
         }
 
         @Override
@@ -1076,6 +1067,15 @@
             return super.inflateLayout(context, layoutResId);
         }
 
+        void relayout(ActivityManager.RunningTaskInfo taskInfo,
+                boolean applyStartTransactionOnDraw) {
+            mRelayoutParams.mRunningTaskInfo = taskInfo;
+            mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
+            mRelayoutParams.mLayoutResId = R.layout.caption_layout;
+            relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
+                    mMockWindowContainerTransaction, mMockView, mRelayoutResult);
+        }
+
         private AdditionalViewContainer addTestViewContainer() {
             final Resources resources = mDecorWindowContext.getResources();
             final int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId);
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
new file mode 100644
index 0000000..5594981
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
@@ -0,0 +1,237 @@
+/*
+ * 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.windowdecor.education
+
+import android.annotation.LayoutRes
+import android.content.Context
+import android.graphics.Point
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import android.testing.TestableLooper
+import android.testing.TestableResources
+import android.view.MotionEvent
+import android.view.View
+import android.view.WindowManager
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipArrowDirection
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class DesktopWindowingEducationTooltipControllerTest : ShellTestCase() {
+  @Mock private lateinit var mockWindowManager: WindowManager
+  @Mock private lateinit var mockViewContainerFactory: AdditionalSystemViewContainer.Factory
+  private lateinit var testableResources: TestableResources
+  private lateinit var testableContext: TestableContext
+  private lateinit var tooltipController: DesktopWindowingEducationTooltipController
+  private val tooltipViewArgumentCaptor = argumentCaptor<View>()
+
+  @Before
+  fun setUp() {
+    MockitoAnnotations.initMocks(this)
+    testableContext = TestableContext(mContext)
+    testableResources =
+        testableContext.orCreateTestableResources.apply {
+          addOverride(R.dimen.desktop_windowing_education_tooltip_padding, 10)
+        }
+    testableContext.addMockSystemService(
+        Context.LAYOUT_INFLATER_SERVICE, context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
+    testableContext.addMockSystemService(WindowManager::class.java, mockWindowManager)
+    tooltipController =
+        DesktopWindowingEducationTooltipController(testableContext, mockViewContainerFactory)
+  }
+
+  @Test
+  fun showEducationTooltip_createsTooltipWithCorrectText() {
+    val tooltipText = "This is a tooltip"
+    val tooltipViewConfig = createTooltipConfig(tooltipText = tooltipText)
+
+    tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123)
+
+    verify(mockViewContainerFactory, times(1))
+        .create(
+            windowManagerWrapper = any(),
+            taskId = anyInt(),
+            x = anyInt(),
+            y = anyInt(),
+            width = anyInt(),
+            height = anyInt(),
+            flags = anyInt(),
+            view = tooltipViewArgumentCaptor.capture())
+    val tooltipTextView =
+        tooltipViewArgumentCaptor.lastValue.findViewById<TextView>(R.id.tooltip_text)
+    assertThat(tooltipTextView.text).isEqualTo(tooltipText)
+  }
+
+  @Test
+  fun showEducationTooltip_usesCorrectTaskIdForWindow() {
+    val tooltipViewConfig = createTooltipConfig()
+    val taskIdArgumentCaptor = argumentCaptor<Int>()
+
+    tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123)
+
+    verify(mockViewContainerFactory, times(1))
+        .create(
+            windowManagerWrapper = any(),
+            taskId = taskIdArgumentCaptor.capture(),
+            x = anyInt(),
+            y = anyInt(),
+            width = anyInt(),
+            height = anyInt(),
+            flags = anyInt(),
+            view = anyOrNull())
+    assertThat(taskIdArgumentCaptor.lastValue).isEqualTo(123)
+  }
+
+  @Test
+  fun showEducationTooltip_tooltipPointsUpwards_horizontallyPositionTooltip() {
+    val initialTooltipX = 0
+    val initialTooltipY = 0
+    val tooltipViewConfig =
+        createTooltipConfig(
+            arrowDirection = TooltipArrowDirection.UP,
+            tooltipViewGlobalCoordinates = Point(initialTooltipX, initialTooltipY))
+    val tooltipXArgumentCaptor = argumentCaptor<Int>()
+    val tooltipWidthArgumentCaptor = argumentCaptor<Int>()
+
+    tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123)
+
+    verify(mockViewContainerFactory, times(1))
+        .create(
+            windowManagerWrapper = any(),
+            taskId = anyInt(),
+            x = tooltipXArgumentCaptor.capture(),
+            y = anyInt(),
+            width = tooltipWidthArgumentCaptor.capture(),
+            height = anyInt(),
+            flags = anyInt(),
+            view = tooltipViewArgumentCaptor.capture())
+    val expectedTooltipX = initialTooltipX - tooltipWidthArgumentCaptor.lastValue / 2
+    assertThat(tooltipXArgumentCaptor.lastValue).isEqualTo(expectedTooltipX)
+  }
+
+  @Test
+  fun showEducationTooltip_tooltipPointsLeft_verticallyPositionTooltip() {
+    val initialTooltipX = 0
+    val initialTooltipY = 0
+    val tooltipViewConfig =
+        createTooltipConfig(
+            arrowDirection = TooltipArrowDirection.LEFT,
+            tooltipViewGlobalCoordinates = Point(initialTooltipX, initialTooltipY))
+    val tooltipYArgumentCaptor = argumentCaptor<Int>()
+    val tooltipHeightArgumentCaptor = argumentCaptor<Int>()
+
+    tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123)
+
+    verify(mockViewContainerFactory, times(1))
+        .create(
+            windowManagerWrapper = any(),
+            taskId = anyInt(),
+            x = anyInt(),
+            y = tooltipYArgumentCaptor.capture(),
+            width = anyInt(),
+            height = tooltipHeightArgumentCaptor.capture(),
+            flags = anyInt(),
+            view = tooltipViewArgumentCaptor.capture())
+    val expectedTooltipY = initialTooltipY - tooltipHeightArgumentCaptor.lastValue / 2
+    assertThat(tooltipYArgumentCaptor.lastValue).isEqualTo(expectedTooltipY)
+  }
+
+  @Test
+  fun showEducationTooltip_touchEventActionOutside_dismissActionPerformed() {
+    val mockLambda: () -> Unit = mock()
+    val tooltipViewConfig = createTooltipConfig(onDismissAction = mockLambda)
+
+    tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123)
+    verify(mockViewContainerFactory, times(1))
+        .create(
+            windowManagerWrapper = any(),
+            taskId = anyInt(),
+            x = anyInt(),
+            y = anyInt(),
+            width = anyInt(),
+            height = anyInt(),
+            flags = anyInt(),
+            view = tooltipViewArgumentCaptor.capture())
+    val motionEvent =
+        MotionEvent.obtain(
+            /* downTime= */ 0L,
+            /* eventTime= */ 0L,
+            MotionEvent.ACTION_OUTSIDE,
+            /* x= */ 0f,
+            /* y= */ 0f,
+            /* metaState= */ 0)
+    tooltipViewArgumentCaptor.lastValue.dispatchTouchEvent(motionEvent)
+
+    verify(mockLambda).invoke()
+  }
+
+  @Test
+  fun showEducationTooltip_tooltipClicked_onClickActionPerformed() {
+    val mockLambda: () -> Unit = mock()
+    val tooltipViewConfig = createTooltipConfig(onEducationClickAction = mockLambda)
+
+    tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123)
+    verify(mockViewContainerFactory, times(1))
+        .create(
+            windowManagerWrapper = any(),
+            taskId = anyInt(),
+            x = anyInt(),
+            y = anyInt(),
+            width = anyInt(),
+            height = anyInt(),
+            flags = anyInt(),
+            view = tooltipViewArgumentCaptor.capture())
+    tooltipViewArgumentCaptor.lastValue.performClick()
+
+    verify(mockLambda).invoke()
+  }
+
+  private fun createTooltipConfig(
+      @LayoutRes tooltipViewLayout: Int = R.layout.desktop_windowing_education_top_arrow_tooltip,
+      tooltipViewGlobalCoordinates: Point = Point(0, 0),
+      tooltipText: String = "This is a tooltip",
+      arrowDirection: TooltipArrowDirection = TooltipArrowDirection.UP,
+      onEducationClickAction: () -> Unit = {},
+      onDismissAction: () -> Unit = {}
+  ) =
+      DesktopWindowingEducationTooltipController.EducationViewConfig(
+          tooltipViewLayout = tooltipViewLayout,
+          tooltipViewGlobalCoordinates = tooltipViewGlobalCoordinates,
+          tooltipText = tooltipText,
+          arrowDirection = arrowDirection,
+          onEducationClickAction = onEducationClickAction,
+          onDismissAction = onDismissAction,
+      )
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt
deleted file mode 100644
index 1b2ce9e..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewhost/DefaultWindowDecorViewHostTest.kt
+++ /dev/null
@@ -1,222 +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.windowdecor.viewhost
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.SurfaceControl
-import android.view.SurfaceControlViewHost
-import android.view.View
-import android.view.WindowManager
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-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.Assert.assertThrows
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.verify
-
-
-/**
- * Tests for [DefaultWindowDecorViewHost].
- *
- * Build/Install/Run:
- * atest WMShellUnitTests:DefaultWindowDecorViewHostTest
- */
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
-class DefaultWindowDecorViewHostTest : ShellTestCase() {
-
-    @Test
-    fun updateView_layoutInViewHost() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
-    }
-
-    @Test
-    fun updateView_alreadyLaidOut_relayouts() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        val otherParams = WindowManager.LayoutParams(200, 200)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = otherParams,
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
-        assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
-            .isEqualTo(otherParams.width)
-    }
-
-    @Test
-    fun updateView_replacingView_throws() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        val otherView = View(context)
-        assertThrows(Exception::class.java) {
-            windowDecorViewHost.updateView(
-                view = otherView,
-                attrs = WindowManager.LayoutParams(100, 100),
-                configuration = context.resources.configuration,
-                onDrawTransaction = null
-            )
-        }
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Test
-    fun updateView_clearsPendingAsyncJob() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val asyncView = View(context)
-        val syncView = View(context)
-        val asyncAttrs = WindowManager.LayoutParams(100, 100)
-        val syncAttrs = WindowManager.LayoutParams(200, 200)
-
-        windowDecorViewHost.updateViewAsync(
-            view = asyncView,
-            attrs = asyncAttrs,
-            configuration = context.resources.configuration,
-        )
-
-        // No view host yet, since the coroutine hasn't run.
-        assertThat(windowDecorViewHost.viewHost).isNull()
-
-        windowDecorViewHost.updateView(
-            view = syncView,
-            attrs = syncAttrs,
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        // Would run coroutine if it hadn't been cancelled.
-        advanceUntilIdle()
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
-        // View host view/attrs should match the ones from the sync call, plus, since the
-        // sync/async were made with different views, if the job hadn't been cancelled there
-        // would've been an exception thrown as replacing views isn't allowed.
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(syncView)
-        assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
-            .isEqualTo(syncAttrs.width)
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Test
-    fun updateViewAsync() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-        val view = View(context)
-        val attrs = WindowManager.LayoutParams(100, 100)
-
-        windowDecorViewHost.updateViewAsync(
-            view = view,
-            attrs = attrs,
-            configuration = context.resources.configuration,
-        )
-
-        assertThat(windowDecorViewHost.viewHost).isNull()
-
-        advanceUntilIdle()
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Test
-    fun updateViewAsync_clearsPendingAsyncJob() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-
-        val view = View(context)
-        windowDecorViewHost.updateViewAsync(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-        )
-        val otherView = View(context)
-        windowDecorViewHost.updateViewAsync(
-            view = otherView,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-        )
-
-        advanceUntilIdle()
-
-        assertThat(windowDecorViewHost.viewHost).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
-        assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(otherView)
-    }
-
-    @Test
-    fun release() = runTest {
-        val windowDecorViewHost = createDefaultViewHost()
-
-        val view = View(context)
-        windowDecorViewHost.updateView(
-            view = view,
-            attrs = WindowManager.LayoutParams(100, 100),
-            configuration = context.resources.configuration,
-            onDrawTransaction = null
-        )
-
-        val t = mock(SurfaceControl.Transaction::class.java)
-        windowDecorViewHost.release(t)
-
-        verify(windowDecorViewHost.viewHost!!).release()
-        verify(t).remove(windowDecorViewHost.surfaceControl)
-    }
-
-    private fun CoroutineScope.createDefaultViewHost() = DefaultWindowDecorViewHost(
-        context = context,
-        mainScope = this,
-        display = context.display,
-        surfaceControlViewHostFactory = { c, d, wwm, s ->
-            spy(SurfaceControlViewHost(c, d, wwm, s))
-        }
-    )
-}
\ No newline at end of file
diff --git a/libs/appfunctions/Android.bp b/libs/appfunctions/Android.bp
index 09e2f42..c6cee07 100644
--- a/libs/appfunctions/Android.bp
+++ b/libs/appfunctions/Android.bp
@@ -29,3 +29,11 @@
     no_dist: true,
     unsafe_ignore_missing_latest_api: true,
 }
+
+prebuilt_etc {
+    name: "appfunctions.sidecar.xml",
+    system_ext_specific: true,
+    sub_dir: "permissions",
+    src: "appfunctions.sidecar.xml",
+    filename_from_src: true,
+}
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index 504e329..3ed33db 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -3,13 +3,15 @@
 
   public final class AppFunctionManager {
     ctor public AppFunctionManager(android.content.Context);
-    method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @Deprecated public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
   }
 
   public abstract class AppFunctionService extends android.app.Service {
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @Deprecated @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
     field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
diff --git a/libs/appfunctions/appfunctions.sidecar.xml b/libs/appfunctions/appfunctions.sidecar.xml
new file mode 100644
index 0000000..bef8b6e
--- /dev/null
+++ b/libs/appfunctions/appfunctions.sidecar.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<permissions>
+    <library
+        name="com.google.android.appfunctions.sidecar"
+        file="/system_ext/framework/com.google.android.appfunctions.sidecar.jar"/>
+</permissions>
\ No newline at end of file
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
index b1dd467..815fe05 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -19,12 +19,12 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.CancellationSignal;
 
 import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
-
 /**
  * Provides app functions related functionalities.
  *
@@ -45,7 +45,7 @@
      *
      * @param context A {@link Context}.
      * @throws java.lang.IllegalStateException if the underlying {@link
-     *   android.app.appfunctions.AppFunctionManager} is not found.
+     *     android.app.appfunctions.AppFunctionManager} is not found.
      */
     public AppFunctionManager(Context context) {
         mContext = Objects.requireNonNull(context);
@@ -66,6 +66,7 @@
     public void executeAppFunction(
             @NonNull ExecuteAppFunctionRequest sidecarRequest,
             @NonNull @CallbackExecutor Executor executor,
+            @NonNull CancellationSignal cancellationSignal,
             @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
         Objects.requireNonNull(sidecarRequest);
         Objects.requireNonNull(executor);
@@ -74,9 +75,40 @@
         android.app.appfunctions.ExecuteAppFunctionRequest platformRequest =
                 SidecarConverter.getPlatformExecuteAppFunctionRequest(sidecarRequest);
         mManager.executeAppFunction(
-                platformRequest, executor, (platformResponse) -> {
-                    callback.accept(SidecarConverter.getSidecarExecuteAppFunctionResponse(
-                            platformResponse));
+                platformRequest,
+                executor,
+                cancellationSignal,
+                (platformResponse) -> {
+                    callback.accept(
+                            SidecarConverter.getSidecarExecuteAppFunctionResponse(
+                                    platformResponse));
                 });
     }
+
+    /**
+     * Executes the app function.
+     *
+     * <p>Proxies request and response to the underlying {@link
+     * android.app.appfunctions.AppFunctionManager#executeAppFunction}, converting the request and
+     * response in the appropriate type required by the function.
+     *
+     * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor,
+     *     CancellationSignal, Consumer)} instead. This method will be removed once usage references
+     *     are updated.
+     */
+    @Deprecated
+    public void executeAppFunction(
+            @NonNull ExecuteAppFunctionRequest sidecarRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+        Objects.requireNonNull(sidecarRequest);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        executeAppFunction(
+                sidecarRequest,
+                executor,
+                new CancellationSignal(),
+                callback);
+    }
 }
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
index 65959df..6023c97 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.CancellationSignal;
 
 import java.util.function.Consumer;
 
@@ -69,10 +70,11 @@
     private final Binder mBinder =
             android.app.appfunctions.AppFunctionService.createBinder(
                     /* context= */ this,
-                    /* onExecuteFunction= */ (platformRequest, callback) -> {
+                    /* onExecuteFunction= */ (platformRequest, cancellationSignal, callback) -> {
                         AppFunctionService.this.onExecuteFunction(
                                 SidecarConverter.getSidecarExecuteAppFunctionRequest(
                                         platformRequest),
+                                cancellationSignal,
                                 (sidecarResponse) -> {
                                     callback.accept(
                                             SidecarConverter.getPlatformExecuteAppFunctionResponse(
@@ -105,9 +107,42 @@
      * result using the callback, no matter if the execution was successful or not.
      *
      * @param request The function execution request.
+     * @param cancellationSignal A {@link CancellationSignal} to cancel the request.
      * @param callback A callback to report back the result.
      */
     @MainThread
+    public void onExecuteFunction(
+            @NonNull ExecuteAppFunctionRequest request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+        onExecuteFunction(request, callback);
+    }
+
+    /**
+     * Called by the system to execute a specific app function.
+     *
+     * <p>This method is triggered when the system requests your AppFunctionService to handle a
+     * particular function you have registered and made available.
+     *
+     * <p>To ensure proper routing of function requests, assign a unique identifier to each
+     * function. This identifier doesn't need to be globally unique, but it must be unique within
+     * your app. For example, a function to order food could be identified as "orderFood". In most
+     * cases this identifier should come from the ID automatically generated by the AppFunctions
+     * SDK. You can determine the specific function to invoke by calling {@link
+     * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
+     *
+     * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
+     * thread and dispatch the result with the given callback. You should always report back the
+     * result using the callback, no matter if the execution was successful or not.
+     *
+     * @param request The function execution request.
+     * @param callback A callback to report back the result.
+     *
+     * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal,
+     *     Consumer)} instead. This method will be removed once usage references are updated.
+     */
+    @MainThread
+    @Deprecated
     public abstract void onExecuteFunction(
             @NonNull ExecuteAppFunctionRequest request,
             @NonNull Consumer<ExecuteAppFunctionResponse> callback);
diff --git a/libs/appfunctions/tests/Android.bp b/libs/appfunctions/tests/Android.bp
new file mode 100644
index 0000000..6f5eff3
--- /dev/null
+++ b/libs/appfunctions/tests/Android.bp
@@ -0,0 +1,41 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "AppFunctionsSidecarTestCases",
+    team: "trendy_team_system_intelligence",
+    static_libs: [
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "androidx.core_core-ktx",
+        "com.google.android.appfunctions.sidecar.impl",
+        "junit",
+        "kotlin-test",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "testables",
+        "testng",
+        "truth",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+}
diff --git a/libs/appfunctions/tests/AndroidManifest.xml b/libs/appfunctions/tests/AndroidManifest.xml
new file mode 100644
index 0000000..9a7d460
--- /dev/null
+++ b/libs/appfunctions/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.appfunctions.sidecar.tests">
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.mock" />
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.google.android.appfunctions.sidecar.tests">
+    </instrumentation>
+</manifest>
diff --git a/libs/appfunctions/tests/AndroidTest.xml b/libs/appfunctions/tests/AndroidTest.xml
new file mode 100644
index 0000000..8251212
--- /dev/null
+++ b/libs/appfunctions/tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for AppFunctions Sidecar Tests">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="AppFunctionsSidecarTestCases.apk" />
+    </target_preparer>
+    <option name="test-tag" value="AppFunctionsSidecarTestCases" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.google.android.appfunctions.sidecar.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt b/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
new file mode 100644
index 0000000..1f9fddd
--- /dev/null
+++ b/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.appfunctions.sidecar.tests
+
+import android.app.appfunctions.ExecuteAppFunctionRequest
+import android.app.appfunctions.ExecuteAppFunctionResponse
+import android.app.appsearch.GenericDocument
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.android.appfunctions.sidecar.SidecarConverter
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SidecarConverterTest {
+    @Test
+    fun getSidecarExecuteAppFunctionRequest_sameContents() {
+        val extras = Bundle()
+        extras.putString("extra", "value")
+        val parameters: GenericDocument =
+            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                .setPropertyLong("testLong", 23)
+                .build()
+        val platformRequest: ExecuteAppFunctionRequest =
+            ExecuteAppFunctionRequest.Builder("targetPkg", "targetFunctionId")
+                .setExtras(extras)
+                .setParameters(parameters)
+                .build()
+
+        val sidecarRequest = SidecarConverter.getSidecarExecuteAppFunctionRequest(platformRequest)
+
+        assertThat(sidecarRequest.targetPackageName).isEqualTo("targetPkg")
+        assertThat(sidecarRequest.functionIdentifier).isEqualTo("targetFunctionId")
+        assertThat(sidecarRequest.parameters).isEqualTo(parameters)
+        assertThat(sidecarRequest.extras.size()).isEqualTo(1)
+        assertThat(sidecarRequest.extras.getString("extra")).isEqualTo("value")
+    }
+
+    @Test
+    fun getPlatformExecuteAppFunctionRequest_sameContents() {
+        val extras = Bundle()
+        extras.putString("extra", "value")
+        val parameters: GenericDocument =
+            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                .setPropertyLong("testLong", 23)
+                .build()
+        val sidecarRequest =
+            com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder(
+                "targetPkg",
+                "targetFunctionId"
+            )
+                .setExtras(extras)
+                .setParameters(parameters)
+                .build()
+
+        val platformRequest = SidecarConverter.getPlatformExecuteAppFunctionRequest(sidecarRequest)
+
+        assertThat(platformRequest.targetPackageName).isEqualTo("targetPkg")
+        assertThat(platformRequest.functionIdentifier).isEqualTo("targetFunctionId")
+        assertThat(platformRequest.parameters).isEqualTo(parameters)
+        assertThat(platformRequest.extras.size()).isEqualTo(1)
+        assertThat(platformRequest.extras.getString("extra")).isEqualTo("value")
+    }
+
+    @Test
+    fun getSidecarExecuteAppFunctionResponse_successResponse_sameContents() {
+        val resultGd: GenericDocument =
+            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                .setPropertyBoolean(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, true)
+                .build()
+        val platformResponse = ExecuteAppFunctionResponse.newSuccess(resultGd, null)
+
+        val sidecarResponse = SidecarConverter.getSidecarExecuteAppFunctionResponse(
+            platformResponse
+        )
+
+        assertThat(sidecarResponse.isSuccess).isTrue()
+        assertThat(
+            sidecarResponse.resultDocument.getProperty(
+                ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE
+            )
+        )
+            .isEqualTo(booleanArrayOf(true))
+        assertThat(sidecarResponse.resultCode).isEqualTo(ExecuteAppFunctionResponse.RESULT_OK)
+        assertThat(sidecarResponse.errorMessage).isNull()
+    }
+
+    @Test
+    fun getSidecarExecuteAppFunctionResponse_errorResponse_sameContents() {
+        val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
+        val platformResponse =
+            ExecuteAppFunctionResponse.newFailure(
+                ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                null,
+                null
+            )
+
+        val sidecarResponse = SidecarConverter.getSidecarExecuteAppFunctionResponse(
+            platformResponse
+        )
+
+        assertThat(sidecarResponse.isSuccess).isFalse()
+        assertThat(sidecarResponse.resultDocument.namespace).isEqualTo(emptyGd.namespace)
+        assertThat(sidecarResponse.resultDocument.id).isEqualTo(emptyGd.id)
+        assertThat(sidecarResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
+        assertThat(sidecarResponse.resultCode)
+            .isEqualTo(ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR)
+        assertThat(sidecarResponse.errorMessage).isNull()
+    }
+
+    @Test
+    fun getPlatformExecuteAppFunctionResponse_successResponse_sameContents() {
+        val resultGd: GenericDocument =
+            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                .setPropertyBoolean(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, true)
+                .build()
+        val sidecarResponse = com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse
+            .newSuccess(resultGd, null)
+
+        val platformResponse = SidecarConverter.getPlatformExecuteAppFunctionResponse(
+            sidecarResponse
+        )
+
+        assertThat(platformResponse.isSuccess).isTrue()
+        assertThat(
+            platformResponse.resultDocument.getProperty(
+                ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE
+            )
+        )
+            .isEqualTo(booleanArrayOf(true))
+        assertThat(platformResponse.resultCode).isEqualTo(ExecuteAppFunctionResponse.RESULT_OK)
+        assertThat(platformResponse.errorMessage).isNull()
+    }
+
+    @Test
+    fun getPlatformExecuteAppFunctionResponse_errorResponse_sameContents() {
+        val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
+        val sidecarResponse =
+            com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse.newFailure(
+                ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                null,
+                null
+            )
+
+        val platformResponse = SidecarConverter.getPlatformExecuteAppFunctionResponse(
+            sidecarResponse
+        )
+
+        assertThat(platformResponse.isSuccess).isFalse()
+        assertThat(platformResponse.resultDocument.namespace).isEqualTo(emptyGd.namespace)
+        assertThat(platformResponse.resultDocument.id).isEqualTo(emptyGd.id)
+        assertThat(platformResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
+        assertThat(platformResponse.resultCode)
+            .isEqualTo(ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR)
+        assertThat(platformResponse.errorMessage).isNull()
+    }
+}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b6476c9..ae46a99 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -50,6 +50,10 @@
 constexpr bool resample_gainmap_regions() {
     return false;
 }
+
+constexpr bool query_global_priority() {
+    return false;
+}
 }  // namespace hwui_flags
 #endif
 
@@ -110,6 +114,7 @@
 bool Properties::hdr10bitPlus = false;
 bool Properties::skipTelemetry = false;
 bool Properties::resampleGainmapRegions = false;
+bool Properties::queryGlobalPriority = false;
 
 int Properties::timeoutMultiplier = 1;
 
@@ -187,6 +192,7 @@
     hdr10bitPlus = hwui_flags::hdr_10bit_plus();
     resampleGainmapRegions = base::GetBoolProperty("debug.hwui.resample_gainmap_regions",
                                                    hwui_flags::resample_gainmap_regions());
+    queryGlobalPriority = hwui_flags::query_global_priority();
 
     timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
     skipTelemetry = base::GetBoolProperty(PROPERTY_SKIP_EGLMANAGER_TELEMETRY,
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index db47152..6f84796 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -346,6 +346,7 @@
     static bool hdr10bitPlus;
     static bool skipTelemetry;
     static bool resampleGainmapRegions;
+    static bool queryGlobalPriority;
 
     static int timeoutMultiplier;
 
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 589abb4..2c23864 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -404,13 +404,19 @@
     }
 }
 
+inline bool RenderNode::isForceInvertDark(TreeInfo& info) {
+    return CC_UNLIKELY(
+             info.forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK);
+}
+
 inline bool RenderNode::shouldEnableForceDark(TreeInfo* info) {
     return CC_UNLIKELY(
             info &&
-            (!info->disableForceDark ||
-             info->forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK));
+            (!info->disableForceDark || isForceInvertDark(*info)));
 }
 
+
+
 void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
     if (!shouldEnableForceDark(info)) {
         return;
@@ -421,7 +427,7 @@
         children.push_back(node);
     });
     if (mDisplayList.hasText()) {
-        if (mDisplayList.hasFill()) {
+        if (isForceInvertDark(*info) && mDisplayList.hasFill()) {
             // Handle a special case for custom views that draw both text and background in the
             // same RenderNode, which would otherwise be altered to white-on-white text.
             usage = UsageHint::Container;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c904542..afbbce7 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -234,6 +234,7 @@
     void syncDisplayList(TreeObserver& observer, TreeInfo* info);
     void handleForceDark(TreeInfo* info);
     bool shouldEnableForceDark(TreeInfo* info);
+    bool isForceInvertDark(TreeInfo& info);
 
     void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
     void pushStagingPropertiesChanges(TreeInfo& info);
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index ab052b9..93df478 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -129,3 +129,13 @@
   description: "APIs that expose gainmap metadata corresponding to those defined in ISO 21496-1"
   bug: "349357636"
 }
+
+flag {
+  name: "query_global_priority"
+  namespace: "core_graphics"
+  description: "Attempt to query whether the vulkan driver supports the requested global priority before queue creation."
+  bug: "343986434"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/libs/hwui/jni/GraphicsStatsService.cpp b/libs/hwui/jni/GraphicsStatsService.cpp
index 54369b9..80a8ae1 100644
--- a/libs/hwui/jni/GraphicsStatsService.cpp
+++ b/libs/hwui/jni/GraphicsStatsService.cpp
@@ -42,8 +42,9 @@
     return reinterpret_cast<jlong>(dump);
 }
 
-static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
-                      jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jint uid,
+                      jstring jpackage, jlong versionCode, jlong startTime, jlong endTime,
+                      jbyteArray jdata) {
     std::string path;
     const ProfileData* data = nullptr;
     LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
@@ -68,7 +69,8 @@
     LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
 
     const std::string package(packageChars.c_str(), packageChars.size());
-    GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
+    GraphicsStatsService::addToDump(dump, path, static_cast<uid_t>(uid), package, versionCode,
+                                    startTime, endTime, data);
 }
 
 static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
@@ -91,7 +93,7 @@
     GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE);
 }
 
-static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
+static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jint uid, jstring jpackage,
                        jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
     ScopedByteArrayRO buffer(env, jdata);
     LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
@@ -106,7 +108,8 @@
     const std::string path(pathChars.c_str(), pathChars.size());
     const std::string package(packageChars.c_str(), packageChars.size());
     const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
-    GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
+    GraphicsStatsService::saveBuffer(path, static_cast<uid_t>(uid), package, versionCode, startTime,
+                                     endTime, data);
 }
 
 static jobject gGraphicsStatsServiceObject = nullptr;
@@ -173,16 +176,16 @@
 } // namespace android
 using namespace android;
 
-static const JNINativeMethod sMethods[] =
-        {{"nGetAshmemSize", "()I", (void*)getAshmemSize},
-         {"nCreateDump", "(IZ)J", (void*)createDump},
-         {"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump},
-         {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump},
-         {"nFinishDump", "(J)V", (void*)finishDump},
-         {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory},
-         {"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer},
-         {"nativeInit", "()V", (void*)nativeInit},
-         {"nativeDestructor", "()V", (void*)nativeDestructor}};
+static const JNINativeMethod sMethods[] = {
+        {"nGetAshmemSize", "()I", (void*)getAshmemSize},
+        {"nCreateDump", "(IZ)J", (void*)createDump},
+        {"nAddToDump", "(JLjava/lang/String;ILjava/lang/String;JJJ[B)V", (void*)addToDump},
+        {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump},
+        {"nFinishDump", "(J)V", (void*)finishDump},
+        {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory},
+        {"nSaveBuffer", "(Ljava/lang/String;ILjava/lang/String;JJJ[B)V", (void*)saveBuffer},
+        {"nativeInit", "()V", (void*)nativeInit},
+        {"nativeDestructor", "()V", (void*)nativeDestructor}};
 
 int register_android_graphics_GraphicsStatsService(JNIEnv* env) {
     jclass graphicsStatsService_class =
diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto
index 745393c..a6e786c 100644
--- a/libs/hwui/protos/graphicsstats.proto
+++ b/libs/hwui/protos/graphicsstats.proto
@@ -58,6 +58,9 @@
 
     // HWUI renders pipeline type: GL or Vulkan
     optional PipelineType pipeline = 8;
+
+    // The UID of the app
+    optional int32 uid = 9;
 }
 
 message GraphicsStatsJankSummaryProto {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e302393..6571d92 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -44,7 +44,7 @@
 namespace renderthread {
 
 // Not all of these are strictly required, but are all enabled if present.
-static std::array<std::string_view, 21> sEnableExtensions{
+static std::array<std::string_view, 23> sEnableExtensions{
         VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
         VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
         VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
@@ -65,6 +65,8 @@
         VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
         VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
         VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME,
+        VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME,
+        VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME,
         VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
 };
 
@@ -206,7 +208,7 @@
     GET_INST_PROC(GetPhysicalDeviceFeatures2);
     GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2);
     GET_INST_PROC(GetPhysicalDeviceProperties);
-    GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
+    GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties2);
 
     uint32_t gpuCount;
     LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr));
@@ -225,21 +227,30 @@
 
     // query to get the initial queue props size
     uint32_t queueCount = 0;
-    mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
+    mGetPhysicalDeviceQueueFamilyProperties2(mPhysicalDevice, &queueCount, nullptr);
     LOG_ALWAYS_FATAL_IF(!queueCount);
 
     // now get the actual queue props
-    std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]);
-    mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, queueProps.get());
+    std::unique_ptr<VkQueueFamilyProperties2[]>
+            queueProps(new VkQueueFamilyProperties2[queueCount]);
+    // query the global priority, this ignored if VK_EXT_global_priority isn't supported
+    std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount);
+    for (uint32_t i = 0; i < queueCount; i++) {
+        queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT;
+        queuePriorityProps[i].pNext = nullptr;
+        queueProps[i].pNext = &queuePriorityProps[i];
+    }
+    mGetPhysicalDeviceQueueFamilyProperties2(mPhysicalDevice, &queueCount, queueProps.get());
 
     constexpr auto kRequestedQueueCount = 2;
 
     // iterate to find the graphics queue
     mGraphicsQueueIndex = queueCount;
     for (uint32_t i = 0; i < queueCount; i++) {
-        if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+        if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
             mGraphicsQueueIndex = i;
-            LOG_ALWAYS_FATAL_IF(queueProps[i].queueCount < kRequestedQueueCount);
+            LOG_ALWAYS_FATAL_IF(
+                    queueProps[i].queueFamilyProperties.queueCount < kRequestedQueueCount);
             break;
         }
     }
@@ -327,6 +338,15 @@
         tailPNext = &formatFeatures->pNext;
     }
 
+    VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT* globalPriorityQueryFeatures =
+            new VkPhysicalDeviceGlobalPriorityQueryFeaturesEXT;
+    globalPriorityQueryFeatures->sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES_EXT;
+    globalPriorityQueryFeatures->pNext = nullptr;
+    globalPriorityQueryFeatures->globalPriorityQuery = false;
+    *tailPNext = globalPriorityQueryFeatures;
+    tailPNext = &globalPriorityQueryFeatures->pNext;
+
     // query to get the physical device features
     mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
     // this looks like it would slow things down,
@@ -341,24 +361,59 @@
 
     if (Properties::contextPriority != 0 &&
         grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
-        memset(&queuePriorityCreateInfo, 0, sizeof(VkDeviceQueueGlobalPriorityCreateInfoEXT));
-        queuePriorityCreateInfo.sType =
-                VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT;
-        queuePriorityCreateInfo.pNext = nullptr;
+        VkQueueGlobalPriorityEXT globalPriority;
         switch (Properties::contextPriority) {
             case EGL_CONTEXT_PRIORITY_LOW_IMG:
-                queuePriorityCreateInfo.globalPriority = VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT;
+                globalPriority = VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT;
                 break;
             case EGL_CONTEXT_PRIORITY_MEDIUM_IMG:
-                queuePriorityCreateInfo.globalPriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT;
+                globalPriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT;
                 break;
             case EGL_CONTEXT_PRIORITY_HIGH_IMG:
-                queuePriorityCreateInfo.globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT;
+                globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT;
                 break;
             default:
                 LOG_ALWAYS_FATAL("Unsupported context priority");
         }
-        queueNextPtr = &queuePriorityCreateInfo;
+
+        // check if the requested priority is reported by the query
+        bool attachGlobalPriority = false;
+        if (uirenderer::Properties::queryGlobalPriority &&
+            globalPriorityQueryFeatures->globalPriorityQuery) {
+            for (uint32_t i = 0; i < queuePriorityProps[mGraphicsQueueIndex].priorityCount; i++) {
+                if (queuePriorityProps[mGraphicsQueueIndex].priorities[i] == globalPriority) {
+                    attachGlobalPriority = true;
+                    break;
+                }
+            }
+        } else {
+            // Querying is not supported so attempt queue creation with requested priority anyways
+            // If the priority turns out not to be supported, the driver *may* fail with
+            // VK_ERROR_NOT_PERMITTED_KHR
+            attachGlobalPriority = true;
+        }
+
+        if (attachGlobalPriority) {
+            memset(&queuePriorityCreateInfo, 0, sizeof(VkDeviceQueueGlobalPriorityCreateInfoEXT));
+            queuePriorityCreateInfo.sType =
+                    VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT;
+            queuePriorityCreateInfo.pNext = nullptr;
+            queuePriorityCreateInfo.globalPriority = globalPriority;
+            queueNextPtr = &queuePriorityCreateInfo;
+        } else {
+            // If globalPriorityQuery is enabled, attempting queue creation with an unsupported
+            // priority will return VK_ERROR_INITIALIZATION_FAILED.
+            //
+            // SysUI and Launcher will request HIGH when SF has RT but it is a known issue that
+            // upstream drm drivers currently lack a way to grant them the granular privileges
+            // they need for HIGH (but not RT) so they will fail queue creation.
+            // For now, drop the unsupported global priority request so that queue creation
+            // succeeds.
+            //
+            // Once that is fixed, this should probably be a fatal error indicating an improper
+            // request or an app needs to get the correct privileges.
+            ALOGW("Requested context priority is not supported by the queue");
+        }
     }
 
     const VkDeviceQueueCreateInfo queueInfo = {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index f042571..a593ec6 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -152,7 +152,7 @@
     VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
     VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
     VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties;
-    VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
+    VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties2> mGetPhysicalDeviceQueueFamilyProperties2;
     VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
     VkPtr<PFN_vkGetPhysicalDeviceImageFormatProperties2> mGetPhysicalDeviceImageFormatProperties2;
     VkPtr<PFN_vkCreateDevice> mCreateDevice;
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index ece5905..702f2a5 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -22,6 +22,7 @@
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 #include <inttypes.h>
 #include <log/log.h>
+#include <stats_annotations.h>
 #include <stats_event.h>
 #include <statslog_hwui.h>
 #include <sys/mman.h>
@@ -45,9 +46,9 @@
 constexpr int sHistogramSize = ProfileData::HistogramSize();
 constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize();
 
-static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package,
-                                      int64_t versionCode, int64_t startTime, int64_t endTime,
-                                      const ProfileData* data);
+static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, uid_t uid,
+                                      const std::string& package, int64_t versionCode,
+                                      int64_t startTime, int64_t endTime, const ProfileData* data);
 static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd);
 
 class FileDescriptor {
@@ -159,15 +160,16 @@
     return success;
 }
 
-bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package,
-                               int64_t versionCode, int64_t startTime, int64_t endTime,
-                               const ProfileData* data) {
+bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, uid_t uid,
+                               const std::string& package, int64_t versionCode, int64_t startTime,
+                               int64_t endTime, const ProfileData* data) {
     if (proto->stats_start() == 0 || proto->stats_start() > startTime) {
         proto->set_stats_start(startTime);
     }
     if (proto->stats_end() == 0 || proto->stats_end() < endTime) {
         proto->set_stats_end(endTime);
     }
+    proto->set_uid(static_cast<int32_t>(uid));
     proto->set_package_name(package);
     proto->set_version_code(versionCode);
     proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ?
@@ -286,6 +288,7 @@
               proto->package_name().c_str(), proto->has_summary());
         return;
     }
+    dprintf(fd, "\nUID: %d", proto->uid());
     dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
     dprintf(fd, "\nVersion: %" PRId64, proto->version_code());
     dprintf(fd, "\nStats since: %" PRId64 "ns", proto->stats_start());
@@ -319,14 +322,15 @@
     dprintf(fd, "\n");
 }
 
-void GraphicsStatsService::saveBuffer(const std::string& path, const std::string& package,
-                                      int64_t versionCode, int64_t startTime, int64_t endTime,
-                                      const ProfileData* data) {
+void GraphicsStatsService::saveBuffer(const std::string& path, uid_t uid,
+                                      const std::string& package, int64_t versionCode,
+                                      int64_t startTime, int64_t endTime, const ProfileData* data) {
     protos::GraphicsStatsProto statsProto;
     if (!parseFromFile(path, &statsProto)) {
         statsProto.Clear();
     }
-    if (!mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data)) {
+    if (!mergeProfileDataIntoProto(&statsProto, uid, package, versionCode, startTime, endTime,
+                                   data)) {
         return;
     }
     // Although we might not have read any data from the file, merging the existing data
@@ -383,7 +387,7 @@
 
 private:
     // use package name and app version for a key
-    typedef std::pair<std::string, int64_t> DumpKey;
+    typedef std::tuple<uid_t, std::string, int64_t> DumpKey;
 
     std::map<DumpKey, protos::GraphicsStatsProto> mStats;
     int mFd;
@@ -392,7 +396,8 @@
 };
 
 void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) {
-    auto dumpKey = std::make_pair(stat.package_name(), stat.version_code());
+    auto dumpKey = std::make_tuple(static_cast<uid_t>(stat.uid()), stat.package_name(),
+                                   stat.version_code());
     auto findIt = mStats.find(dumpKey);
     if (findIt == mStats.end()) {
         mStats[dumpKey] = stat;
@@ -437,15 +442,15 @@
     return new Dump(outFd, type);
 }
 
-void GraphicsStatsService::addToDump(Dump* dump, const std::string& path,
+void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, uid_t uid,
                                      const std::string& package, int64_t versionCode,
                                      int64_t startTime, int64_t endTime, const ProfileData* data) {
     protos::GraphicsStatsProto statsProto;
     if (!path.empty() && !parseFromFile(path, &statsProto)) {
         statsProto.Clear();
     }
-    if (data &&
-        !mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data)) {
+    if (data && !mergeProfileDataIntoProto(&statsProto, uid, package, versionCode, startTime,
+                                           endTime, data)) {
         return;
     }
     if (!statsProto.IsInitialized()) {
@@ -556,6 +561,8 @@
         // TODO: fill in UI mainline module version, when the feature is available.
         AStatsEvent_writeInt64(event, (int64_t)0);
         AStatsEvent_writeBool(event, !lastFullDay);
+        AStatsEvent_writeInt32(event, stat.uid());
+        AStatsEvent_addBoolAnnotation(event, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
         AStatsEvent_build(event);
     }
     delete dump;
diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h
index 4063f74..68c7355 100644
--- a/libs/hwui/service/GraphicsStatsService.h
+++ b/libs/hwui/service/GraphicsStatsService.h
@@ -44,13 +44,14 @@
         ProtobufStatsd,
     };
 
-    static void saveBuffer(const std::string& path, const std::string& package, int64_t versionCode,
-                           int64_t startTime, int64_t endTime, const ProfileData* data);
+    static void saveBuffer(const std::string& path, uid_t uid, const std::string& package,
+                           int64_t versionCode, int64_t startTime, int64_t endTime,
+                           const ProfileData* data);
 
     static Dump* createDump(int outFd, DumpType type);
-    static void addToDump(Dump* dump, const std::string& path, const std::string& package,
-                          int64_t versionCode, int64_t startTime, int64_t endTime,
-                          const ProfileData* data);
+    static void addToDump(Dump* dump, const std::string& path, uid_t uid,
+                          const std::string& package, int64_t versionCode, int64_t startTime,
+                          int64_t endTime, const ProfileData* data);
     static void addToDump(Dump* dump, const std::string& path);
     static void finishDump(Dump* dump);
     static void finishDumpInMemory(Dump* dump, AStatsEventList* data, bool lastFullDay);
diff --git a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
index c2d23e6..eb164f9 100644
--- a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
+++ b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
@@ -62,6 +62,7 @@
 
 TEST(GraphicsStats, saveLoad) {
     std::string path = findRootPath() + "/test_saveLoad";
+    uid_t uid = 123;
     std::string packageName = "com.test.saveLoad";
     MockProfileData mockData;
     mockData.editJankFrameCount() = 20;
@@ -75,12 +76,13 @@
     for (size_t i = 0; i < mockData.editSlowFrameCounts().size(); i++) {
         mockData.editSlowFrameCounts()[i] = (i % 5) + 1;
     }
-    GraphicsStatsService::saveBuffer(path, packageName, 5, 3000, 7000, &mockData);
+    GraphicsStatsService::saveBuffer(path, uid, packageName, 5, 3000, 7000, &mockData);
     protos::GraphicsStatsProto loadedProto;
     EXPECT_TRUE(GraphicsStatsService::parseFromFile(path, &loadedProto));
     // Clean up the file
     unlink(path.c_str());
 
+    EXPECT_EQ(uid, loadedProto.uid());
     EXPECT_EQ(packageName, loadedProto.package_name());
     EXPECT_EQ(5, loadedProto.version_code());
     EXPECT_EQ(3000, loadedProto.stats_start());
@@ -109,6 +111,7 @@
 TEST(GraphicsStats, merge) {
     std::string path = findRootPath() + "/test_merge";
     std::string packageName = "com.test.merge";
+    uid_t uid = 123;
     MockProfileData mockData;
     mockData.editJankFrameCount() = 20;
     mockData.editTotalFrameCount() = 100;
@@ -121,7 +124,7 @@
     for (size_t i = 0; i < mockData.editSlowFrameCounts().size(); i++) {
         mockData.editSlowFrameCounts()[i] = (i % 5) + 1;
     }
-    GraphicsStatsService::saveBuffer(path, packageName, 5, 3000, 7000, &mockData);
+    GraphicsStatsService::saveBuffer(path, uid, packageName, 5, 3000, 7000, &mockData);
     mockData.editJankFrameCount() = 50;
     mockData.editTotalFrameCount() = 500;
     for (size_t i = 0; i < mockData.editFrameCounts().size(); i++) {
@@ -130,13 +133,15 @@
     for (size_t i = 0; i < mockData.editSlowFrameCounts().size(); i++) {
         mockData.editSlowFrameCounts()[i] = ((i % 10) + 1) * 2;
     }
-    GraphicsStatsService::saveBuffer(path, packageName, 5, 7050, 10000, &mockData);
+
+    GraphicsStatsService::saveBuffer(path, uid, packageName, 5, 7050, 10000, &mockData);
 
     protos::GraphicsStatsProto loadedProto;
     EXPECT_TRUE(GraphicsStatsService::parseFromFile(path, &loadedProto));
     // Clean up the file
     unlink(path.c_str());
 
+    EXPECT_EQ(uid, loadedProto.uid());
     EXPECT_EQ(packageName, loadedProto.package_name());
     EXPECT_EQ(5, loadedProto.version_code());
     EXPECT_EQ(3000, loadedProto.stats_start());
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 9af6b28..8394daf 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -709,6 +709,10 @@
     @EnforcePermission("MODIFY_AUDIO_ROUTING")
     List<AudioFocusInfo> getFocusStack();
 
+    @EnforcePermission("MODIFY_AUDIO_ROUTING")
+    oneway void sendFocusLossAndUpdate(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
+
+    @EnforcePermission("MODIFY_AUDIO_ROUTING")
     boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
 
     @EnforcePermission("MODIFY_AUDIO_ROUTING")
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 293a8f8..2c8e352 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -927,6 +927,29 @@
     }
 
     /**
+     * @hide
+     * Causes the given audio focus owner to lose audio focus with
+     * {@link android.media.AudioManager#AUDIOFOCUS_LOSS}, and be removed from the focus stack.
+     * Unlike {@link #sendFocusLoss(AudioFocusInfo)}, the method causes the focus stack
+     * to be reevaluated as the discarded focus owner may have been at the top of stack,
+     * and now the new owner needs to be notified of the gain.
+     * @param focusLoser identifies the focus owner to discard from the focus stack
+     * @throws IllegalStateException if used on an unregistered policy, or a registered policy
+     * with no {@link AudioPolicyFocusListener} set
+     * @see #getFocusStack()
+     * @see #sendFocusLoss(AudioFocusInfo)
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void sendFocusLossAndUpdate(@NonNull AudioFocusInfo focusLoser)
+            throws IllegalStateException {
+        try {
+            getService().sendFocusLossAndUpdate(Objects.requireNonNull(focusLoser), cb());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus
      * loss, and for it to exit the focus stack (its focus listener will not be invoked after that).
      * This operation is only valid for a registered policy (with
diff --git a/media/java/android/media/flags/editing.aconfig b/media/java/android/media/flags/editing.aconfig
index bf6ec96..185f579 100644
--- a/media/java/android/media/flags/editing.aconfig
+++ b/media/java/android/media/flags/editing.aconfig
@@ -8,3 +8,10 @@
   description: "Add media metrics for transcoding/editing events."
   bug: "297487694"
 }
+
+flag {
+  name: "stagefrightrecorder_enable_b_frames"
+  namespace: "media_solutions"
+  description: "Enable B frames for Stagefright recorder."
+  bug: "341121900"
+}
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index 244292d..bfc1324 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -393,6 +393,16 @@
 
     /**
      * Opens a Bluetooth MIDI device for reading and writing.
+     * Bluetooth MIDI devices are only available after openBluetoothDevice() is called.
+     * Once that happens anywhere in the system, then the BLE-MIDI device will appear as just
+     * another MidiDevice to other apps.
+     *
+     * If the device opened using openBluetoothDevice()  is closed, then it will no longer be
+     * available. To other apps, it will appear as if the BLE MidiDevice had been unplugged.
+     * If a MidiDevice is garbage collected then it will be closed automatically.
+     * If you want the BLE-MIDI device to remain available you should keep the object alive.
+     *
+     * You may close the device with MidiDevice.close().
      *
      * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} to open as a MIDI device
      * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called to receive the
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 31f8996..ef4c3ef 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -16,6 +16,8 @@
 
 package android.media.projection;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
@@ -29,6 +31,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -70,6 +73,7 @@
     private final DisplayManager mDisplayManager;
     @NonNull
     private final Map<Callback, CallbackRecord> mCallbacks = new ArrayMap<>();
+    private final int mDisplayId;
 
     /** @hide */
     public MediaProjection(Context context, IMediaProjection impl) {
@@ -88,6 +92,11 @@
             throw new RuntimeException("Failed to start media projection", e);
         }
         mDisplayManager = displayManager;
+
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        mDisplayId = userManager.isVisibleBackgroundUsersSupported()
+                ? userManager.getMainDisplayIdAssignedToUser()
+                : DEFAULT_DISPLAY;
     }
 
     /**
@@ -156,6 +165,7 @@
         if (surface != null) {
             builder.setSurface(surface);
         }
+        builder.setDisplayIdToMirror(mDisplayId);
         return createVirtualDisplay(builder, callback, handler);
     }
 
@@ -234,6 +244,7 @@
         if (surface != null) {
             builder.setSurface(surface);
         }
+        builder.setDisplayIdToMirror(mDisplayId);
         return createVirtualDisplay(builder, callback, handler);
     }
 
diff --git a/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java b/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java
index c376f25..8b28347 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerInstrumentation.java
@@ -17,6 +17,7 @@
 package android.media.soundtrigger;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -328,6 +329,7 @@
          * Get the recognition config used to start this recognition.
          * @return - The config passed to the HAL for startRecognition.
          */
+        @FlaggedApi(Flags.FLAG_MANAGER_API)
         public @NonNull SoundTrigger.RecognitionConfig getRecognitionConfig() {
             return mRecognitionConfig;
         }
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 78352871..3d0c406 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -19,6 +19,7 @@
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -42,7 +43,6 @@
 import android.media.permission.Identity;
 import android.media.permission.SafeCloseable;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -463,13 +463,18 @@
     public static final String EXTRA_STATUS = "android.media.soundtrigger.STATUS";
 
     /**
-     * Loads a given sound model into the sound trigger. Note the model will be unloaded if there is
-     * an error/the system service is restarted.
-     * @hide
+     * Loads a given sound model into the sound trigger.
+     *
+     * <p><b>Note:</b> the model will be unloaded if there is an error/the system service is
+     * restarted.
+     *
+     * @return {@link SoundTrigger#STATUS_OK} if the model was loaded successfully, error code
+     *         otherwise
      */
+    @SuppressLint("AndroidFrameworkRequiresPermission")
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
-    @TestApi
+    @FlaggedApi(Flags.FLAG_MANAGER_API)
     public int loadSoundModel(@NonNull SoundModel soundModel) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
@@ -508,11 +513,11 @@
      *
      * @return {@link SoundTrigger#STATUS_OK} if the recognition could be started, error code
      *         otherwise
-     *
-     * @hide
      */
+    @SuppressLint("AndroidFrameworkRequiresPermission")
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
+    @FlaggedApi(Flags.FLAG_MANAGER_API)
     public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params,
         @NonNull ComponentName detectionService, @NonNull RecognitionConfig config) {
         Objects.requireNonNull(soundModelId);
@@ -531,11 +536,15 @@
 
     /**
      * Stops the given model's recognition.
-     * @hide
+     *
+     * @return {@link SoundTrigger#STATUS_OK} if the recognition could be stopped, error code
+     *         otherwise
      */
+    @SuppressLint("AndroidFrameworkRequiresPermission")
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int stopRecognition(UUID soundModelId) {
+    @UnsupportedAppUsage
+    @FlaggedApi(Flags.FLAG_MANAGER_API)
+    public int stopRecognition(@NonNull UUID soundModelId) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
         }
@@ -548,12 +557,18 @@
     }
 
     /**
-     * Removes the given model from memory. Will also stop any pending recognitions.
-     * @hide
+     * Removes the given model from memory.
+     *
+     * <p>Will also stop any pending recognitions.
+     *
+     * @return {@link SoundTrigger#STATUS_OK} if the model was unloaded successfully, error code
+     *         otherwise
      */
+    @SuppressLint("AndroidFrameworkRequiresPermission")
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public int unloadSoundModel(UUID soundModelId) {
+    @UnsupportedAppUsage
+    @FlaggedApi(Flags.FLAG_MANAGER_API)
+    public int unloadSoundModel(@NonNull UUID soundModelId) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
         }
@@ -566,12 +581,13 @@
     }
 
     /**
-     * Returns true if the given model has had detection started on it.
-     * @hide
+     * Returns whether the given model has had detection started on it.
      */
+    @SuppressLint("AndroidFrameworkRequiresPermission")
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
-    public boolean isRecognitionActive(UUID soundModelId) {
+    @FlaggedApi(Flags.FLAG_MANAGER_API)
+    public boolean isRecognitionActive(@NonNull UUID soundModelId) {
         if (soundModelId == null || mSoundTriggerSession == null) {
             return false;
         }
@@ -599,14 +615,16 @@
     }
 
     /**
-     * Asynchronously get state of the indicated model.  The model state is returned as
-     * a recognition event in the callback that was registered in the startRecognition
-     * method.
-     * @hide
+     * Asynchronously gets state of the indicated model.
+     *
+     * <p>The model state is returned as a recognition event in the callback that was registered
+     * in the {@link #startRecognition(UUID, Bundle, ComponentName, RecognitionConfig)} method.
      */
+    @SuppressLint("AndroidFrameworkRequiresPermission")
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
-    public int getModelState(UUID soundModelId) {
+    @FlaggedApi(Flags.FLAG_MANAGER_API)
+    public int getModelState(@NonNull UUID soundModelId) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
         }
@@ -621,7 +639,7 @@
     }
 
     /**
-     * Get the hardware sound trigger module properties currently loaded.
+     * Gets the hardware sound trigger module properties currently loaded.
      *
      * @return The properties currently loaded. Returns null if no supported hardware loaded.
      */
@@ -639,9 +657,11 @@
     }
 
     /**
-     * Set a model specific {@link ModelParams} with the given value. This
-     * parameter will keep its value for the duration the model is loaded regardless of starting and
-     * stopping recognition. Once the model is unloaded, the value will be lost.
+     * Sets a model specific {@link ModelParams} with the given value.
+     *
+     * <p>This parameter will keep its value for the duration the model is loaded regardless of
+     * starting and stopping recognition. Once the model is unloaded, the value will be lost.
+     *
      * {@link SoundTriggerManager#queryParameter} should be checked first before calling this
      * method.
      *
@@ -671,12 +691,15 @@
     }
 
     /**
-     * Get a model specific {@link ModelParams}. This parameter will keep its value
-     * for the duration the model is loaded regardless of starting and stopping recognition.
-     * Once the model is unloaded, the value will be lost. If the value is not set, a default
-     * value is returned. See {@link ModelParams} for parameter default values.
-     * {@link SoundTriggerManager#queryParameter} should be checked first before
-     * calling this method. Otherwise, an exception can be thrown.
+     * Gets a model specific {@link ModelParams}.
+     *
+     * <p>This parameter will keep its value for the duration the model is loaded regardless
+     * of starting and stopping recognition. Once the model is unloaded, the value will be lost.
+     * If the value is not set, a default value is returned. See {@link ModelParams} for
+     * parameter default values.
+     *
+     * {@link SoundTriggerManager#queryParameter} should be checked first before calling this
+     * method. Otherwise, an exception can be thrown.
      *
      * @param soundModelId UUID of model to get parameter
      * @param modelParam   {@link ModelParams}
@@ -697,9 +720,10 @@
     }
 
     /**
-     * Determine if parameter control is supported for the given model handle.
-     * This method should be checked prior to calling {@link SoundTriggerManager#setParameter} or
-     * {@link SoundTriggerManager#getParameter}.
+     * Determines if parameter control is supported for the given model handle.
+     *
+     * <p>This method should be checked prior to calling {@link SoundTriggerManager#setParameter}
+     * or {@link SoundTriggerManager#getParameter}.
      *
      * @param soundModelId handle of model to get parameter
      * @param modelParam {@link ModelParams}
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 9603c0a..d17a9b6 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -60,7 +60,7 @@
     method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
-    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOn(int);
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOnMode(int);
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
     field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int DISABLE = 0; // 0x0
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index de85f1e..951702c 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -258,7 +258,7 @@
     /**
      * Mandatory String extra field in {@link #ACTION_TRANSACTION_DETECTED}
      * Indicates the Secure Element on which the transaction occurred.
-     * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
+     * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC/EUICC, etc.
      */
     public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
 
@@ -560,13 +560,13 @@
     public @interface TagIntentAppPreferenceResult {}
 
     /**
-     * Mode Type for {@link NfcOemExtension#setControllerAlwaysOn(int)}.
+     * Mode Type for {@link NfcOemExtension#setControllerAlwaysOnMode(int)}.
      * @hide
      */
     public static final int CONTROLLER_ALWAYS_ON_MODE_DEFAULT = 1;
 
     /**
-     * Mode Type for {@link NfcOemExtension#setControllerAlwaysOn(int)}.
+     * Mode Type for {@link NfcOemExtension#setControllerAlwaysOnMode(int)}.
      * @hide
      */
     public static final int CONTROLLER_ALWAYS_ON_DISABLE = 0;
@@ -733,7 +733,7 @@
      *
      * @return List<String> containing secure elements on the device which supports
      *                      off host card emulation. eSE for Embedded secure element,
-     *                      SIM for UICC, eSIM for EUICC and so on.
+     *                      SIM for UICC/EUICC and so on.
      * @hide
      */
     public @NonNull List<String> getSupportedOffHostSecureElements() {
@@ -753,11 +753,6 @@
         if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE)) {
             offHostSE.add("eSE");
         }
-        if (Flags.enableCardEmulationEuicc()
-                && callServiceReturn(
-                        () -> sCardEmulationService.isEuiccSupported(), false)) {
-            offHostSE.add("eSIM");
-        }
         return offHostSE;
     }
 
@@ -2328,7 +2323,7 @@
      * <p>This API is for the NFCC internal state management. It allows to discriminate
      * the controller function from the NFC function by keeping the NFC controller on without
      * any NFC RF enabled if necessary.
-     * <p>This call is asynchronous. Register a listener {@link #ControllerAlwaysOnListener}
+     * <p>This call is asynchronous. Register a listener {@link ControllerAlwaysOnListener}
      * by {@link #registerControllerAlwaysOnListener} to find out when the operation is
      * complete.
      * <p>If this returns true, then either NFCC always on state has been set based on the value,
@@ -2342,7 +2337,7 @@
      * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
      * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
      * are unavailable
-     * @return true if feature is supported by the device and operation has bee initiated,
+     * @return true if feature is supported by the device and operation has been initiated,
      * false if the feature is not supported by the device.
      * @hide
      */
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 45038d4..011c60b 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -70,7 +70,7 @@
     private boolean mRfDiscoveryStarted = false;
 
     /**
-     * Mode Type for {@link #setControllerAlwaysOn(int)}.
+     * Mode Type for {@link #setControllerAlwaysOnMode(int)}.
      * Enables the controller in default mode when NFC is disabled (existing API behavior).
      * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}.
      * @hide
@@ -80,7 +80,7 @@
     public static final int ENABLE_DEFAULT = NfcAdapter.CONTROLLER_ALWAYS_ON_MODE_DEFAULT;
 
     /**
-     * Mode Type for {@link #setControllerAlwaysOn(int)}.
+     * Mode Type for {@link #setControllerAlwaysOnMode(int)}.
      * Enables the controller in transparent mode when NFC is disabled.
      * @hide
      */
@@ -89,7 +89,7 @@
     public static final int ENABLE_TRANSPARENT = 2;
 
     /**
-     * Mode Type for {@link #setControllerAlwaysOn(int)}.
+     * Mode Type for {@link #setControllerAlwaysOnMode(int)}.
      * Enables the controller and initializes and enables the EE subsystem when NFC is disabled.
      * @hide
      */
@@ -98,7 +98,7 @@
     public static final int ENABLE_EE = 3;
 
     /**
-     * Mode Type for {@link #setControllerAlwaysOn(int)}.
+     * Mode Type for {@link #setControllerAlwaysOnMode(int)}.
      * Disable the Controller Always On Mode.
      * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}.
      * @hide
@@ -108,7 +108,7 @@
     public static final int DISABLE = NfcAdapter.CONTROLLER_ALWAYS_ON_DISABLE;
 
     /**
-     * Possible controller modes for {@link #setControllerAlwaysOn(int)}.
+     * Possible controller modes for {@link #setControllerAlwaysOnMode(int)}.
      *
      * @hide
      */
@@ -449,6 +449,9 @@
      * <p>This call is asynchronous, register listener {@link NfcAdapter.ControllerAlwaysOnListener}
      * by {@link NfcAdapter#registerControllerAlwaysOnListener} to find out when the operation is
      * complete.
+     * <p> Note: This adds more always on modes on top of existing
+     * {@link NfcAdapter#setControllerAlwaysOn(boolean)} API which can be used to set the NFCC in
+     * only {@link #ENABLE_DEFAULT} and {@link #DISABLE} modes.
      * @param mode one of {@link ControllerMode} modes
      * @throws UnsupportedOperationException if
      *   <li> if FEATURE_NFC, FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
@@ -456,11 +459,12 @@
      *   are unavailable </li>
      *   <li> if the feature is unavailable @see NfcAdapter#isNfcControllerAlwaysOnSupported() </li>
      * @hide
+     * @see NfcAdapter#setControllerAlwaysOn(boolean)
      */
     @SystemApi
     @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
-    public void setControllerAlwaysOn(@ControllerMode int mode) {
+    public void setControllerAlwaysOnMode(@ControllerMode int mode) {
         if (!NfcAdapter.sHasNfcFeature && !NfcAdapter.sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index b28237c..2983875 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -331,8 +331,6 @@
                         mOffHostName = "eSE1";
                     } else if (mOffHostName.equals("SIM")) {
                         mOffHostName = "SIM1";
-                    } else if (Flags.enableCardEmulationEuicc() && mOffHostName.equals("eSIM")) {
-                        mOffHostName = "eSIM1";
                     }
                 }
                 mStaticOffHostName = mOffHostName;
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 83ad32c..4be082c 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -548,13 +548,11 @@
 
         List<String> validSE = adapter.getSupportedOffHostSecureElements();
         if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE"))
-                || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))
-                || (offHostSecureElement.startsWith("eSIM") && !validSE.contains("eSIM"))) {
+                || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) {
             return false;
         }
 
-        if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")
-                && !(Flags.enableCardEmulationEuicc() && offHostSecureElement.startsWith("eSIM"))) {
+        if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")) {
             return false;
         }
 
@@ -562,8 +560,6 @@
             offHostSecureElement = "eSE1";
         } else if (offHostSecureElement.equals("SIM")) {
             offHostSecureElement = "SIM1";
-        } else if (Flags.enableCardEmulationEuicc() && offHostSecureElement.equals("eSIM")) {
-            offHostSecureElement = "eSIM1";
         }
         final String offHostSecureElementV = new String(offHostSecureElement);
         return callServiceReturn(() ->
diff --git a/packages/CarrierDefaultApp/res/values-bs/strings.xml b/packages/CarrierDefaultApp/res/values-bs/strings.xml
index 4fad224..bc725fe 100644
--- a/packages/CarrierDefaultApp/res/values-bs/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-bs/strings.xml
@@ -16,7 +16,7 @@
     <string name="ssl_error_continue" msgid="1138548463994095584">"Ipak nastavi preko preglednika"</string>
     <string name="performance_boost_notification_channel" msgid="3475440855635538592">"Pojačavanje performansi"</string>
     <string name="performance_boost_notification_title" msgid="3126203390685781861">"Opcije 5G mreže operatera"</string>
-    <string name="performance_boost_notification_detail" msgid="216569851036236346">"Posjetite web lokaciju operatera %s da vidite opcije za iskustvo u aplikaciji"</string>
+    <string name="performance_boost_notification_detail" msgid="216569851036236346">"Posjetite web lokaciju operatera %s da vidite opcije za iskustvo s aplikacijom"</string>
     <string name="performance_boost_notification_button_not_now" msgid="6459755324243683785">"Ne sada"</string>
     <string name="performance_boost_notification_button_manage" msgid="4976836444046497973">"Upravljajte"</string>
     <string name="slice_purchase_app_label" msgid="7170191659233241166">"Kupite pojačavanje performansi."</string>
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index 71ecb4c..37a0e79 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -19,6 +19,7 @@
 
     static_libs: [
         "androidx.preference_preference",
+        "SettingsLibSettingsTheme",
     ],
 
     sdk_version: "system_current",
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v35/settingslib_expressive_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v35/settingslib_expressive_action_buttons.xml
new file mode 100644
index 0000000..3e73ebd
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v35/settingslib_expressive_action_buttons.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="@dimen/settingslib_expressive_space_extrasmall4"
+    android:paddingHorizontal="@dimen/settingslib_expressive_space_extrasmall4"
+    android:orientation="horizontal">
+
+    <LinearLayout
+        android:id="@+id/action1"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/button1"
+            style="@style/SettingsLibActionButton.Expressive"
+            android:layout_width="@dimen/settingslib_expressive_space_large3"
+            android:layout_height="@dimen/settingslib_expressive_space_medium5"
+            android:layout_gravity="center_horizontal"
+            android:importantForAccessibility="no"/>
+        <TextView
+            android:id="@+id/text1"
+            style="@style/SettingsLibActionButton.Expressive.Label"
+            android:layout_marginTop="@dimen/settingslib_expressive_space_extrasmall3"
+            android:importantForAccessibility="no"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/action2"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/button2"
+            style="@style/SettingsLibActionButton.Expressive"
+            android:layout_width="@dimen/settingslib_expressive_space_large3"
+            android:layout_height="@dimen/settingslib_expressive_space_medium5"
+            android:layout_gravity="center_horizontal"
+            android:importantForAccessibility="no"/>
+        <TextView
+            android:id="@+id/text2"
+            style="@style/SettingsLibActionButton.Expressive.Label"
+            android:layout_marginTop="@dimen/settingslib_expressive_space_extrasmall3"
+            android:importantForAccessibility="no"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/action3"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/button3"
+            style="@style/SettingsLibActionButton.Expressive"
+            android:layout_width="@dimen/settingslib_expressive_space_large3"
+            android:layout_height="@dimen/settingslib_expressive_space_medium5"
+            android:layout_gravity="center_horizontal"
+            android:importantForAccessibility="no"/>
+        <TextView
+            android:id="@+id/text3"
+            style="@style/SettingsLibActionButton.Expressive.Label"
+            android:layout_marginTop="@dimen/settingslib_expressive_space_extrasmall3"
+            android:importantForAccessibility="no"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/action4"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/button4"
+            style="@style/SettingsLibActionButton.Expressive"
+            android:layout_width="@dimen/settingslib_expressive_space_large3"
+            android:layout_height="@dimen/settingslib_expressive_space_medium5"
+            android:layout_gravity="center_horizontal"
+            android:importantForAccessibility="no"/>
+        <TextView
+            android:id="@+id/text4"
+            style="@style/SettingsLibActionButton.Expressive.Label"
+            android:layout_marginTop="@dimen/settingslib_expressive_space_extrasmall3"
+            android:importantForAccessibility="no"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
new file mode 100644
index 0000000..cc948a6
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<resources>
+    <style name="SettingsLibActionButton.Expressive" parent="SettingsLibButtonStyle.Expressive.Tonal">
+        <item name="android:backgroundTint">@color/settingslib_materialColorPrimaryContainer</item>
+        <item name="iconTint">@color/settingslib_materialColorOnPrimaryContainer</item>
+        <item name="iconGravity">textTop</item>
+    </style>
+
+    <style name="SettingsLibActionButton.Expressive.Label" parent="SettingsLibTextAppearance.Emphasized.Title.Small">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:minWidth">@dimen/settingslib_expressive_space_small3</item>
+        <item name="android:minHeight">@dimen/settingslib_expressive_space_small3</item>
+        <item name="android:textColor">@color/settingslib_materialColorOnSurface</item>
+        <item name="android:layout_gravity">center</item>
+    </style>
+
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index 5dc11cf..b286182 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -26,6 +26,8 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.annotation.DrawableRes;
 import androidx.annotation.StringRes;
@@ -34,6 +36,8 @@
 
 import com.android.settingslib.widget.preference.actionbuttons.R;
 
+import com.google.android.material.button.MaterialButton;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -98,7 +102,10 @@
     }
 
     private void init() {
-        setLayoutResource(R.layout.settingslib_action_buttons);
+        int resId = SettingsThemeHelper.isExpressiveTheme(getContext())
+                ? R.layout.settingslib_expressive_action_buttons
+                : R.layout.settingslib_action_buttons;
+        setLayoutResource(resId);
         setSelectable(false);
 
         final Resources res = getContext().getResources();
@@ -127,6 +134,21 @@
         mButton3Info.mButton = (Button) holder.findViewById(R.id.button3);
         mButton4Info.mButton = (Button) holder.findViewById(R.id.button4);
 
+        if (SettingsThemeHelper.isExpressiveTheme(getContext())) {
+            mButton1Info.mIsExpressive = true;
+            mButton1Info.mTextView = (TextView) holder.findViewById(R.id.text1);
+            mButton1Info.mActionLayout = (LinearLayout) holder.findViewById(R.id.action1);
+            mButton2Info.mIsExpressive = true;
+            mButton2Info.mTextView = (TextView) holder.findViewById(R.id.text2);
+            mButton2Info.mActionLayout = (LinearLayout) holder.findViewById(R.id.action2);
+            mButton3Info.mIsExpressive = true;
+            mButton3Info.mTextView = (TextView) holder.findViewById(R.id.text3);
+            mButton3Info.mActionLayout = (LinearLayout) holder.findViewById(R.id.action3);
+            mButton4Info.mIsExpressive = true;
+            mButton4Info.mTextView = (TextView) holder.findViewById(R.id.text4);
+            mButton4Info.mActionLayout = (LinearLayout) holder.findViewById(R.id.action4);
+        }
+
         mDivider1 = holder.findViewById(R.id.divider1);
         mDivider2 = holder.findViewById(R.id.divider2);
         mDivider3 = holder.findViewById(R.id.divider3);
@@ -169,45 +191,47 @@
             mVisibleButtonInfos.add(mButton4Info);
         }
 
-        final boolean isRtl = getContext().getResources().getConfiguration()
-                .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        switch (mVisibleButtonInfos.size()) {
-            case SINGLE_BUTTON_STYLE :
-                if (isRtl) {
-                    setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle1);
-                } else {
-                    setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle1);
-                }
-                break;
-            case TWO_BUTTONS_STYLE :
-                if (isRtl) {
-                    setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle2);
-                } else {
-                    setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle2);
-                }
-                break;
-            case THREE_BUTTONS_STYLE :
-                if (isRtl) {
-                    setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle3);
-                } else {
-                    setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle3);
-                }
-                break;
-            case FOUR_BUTTONS_STYLE :
-                if (isRtl) {
-                    setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle4);
-                } else {
-                    setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle4);
-                }
-                break;
-            default:
-                Log.e(TAG, "No visible buttons info, skip background settings.");
-                break;
-        }
+        if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
+            final boolean isRtl = getContext().getResources().getConfiguration()
+                    .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            switch (mVisibleButtonInfos.size()) {
+                case SINGLE_BUTTON_STYLE :
+                    if (isRtl) {
+                        setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle1);
+                    } else {
+                        setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle1);
+                    }
+                    break;
+                case TWO_BUTTONS_STYLE :
+                    if (isRtl) {
+                        setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle2);
+                    } else {
+                        setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle2);
+                    }
+                    break;
+                case THREE_BUTTONS_STYLE :
+                    if (isRtl) {
+                        setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle3);
+                    } else {
+                        setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle3);
+                    }
+                    break;
+                case FOUR_BUTTONS_STYLE :
+                    if (isRtl) {
+                        setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle4);
+                    } else {
+                        setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle4);
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "No visible buttons info, skip background settings.");
+                    break;
+            }
 
-        setupDivider1();
-        setupDivider2();
-        setupDivider3();
+            setupDivider1();
+            setupDivider2();
+            setupDivider3();
+        }
     }
 
     private void setupBackgrounds(
@@ -509,23 +533,45 @@
 
     static class ButtonInfo {
         private Button mButton;
+        private TextView mTextView;
+        private LinearLayout mActionLayout;
         private CharSequence mText;
         private Drawable mIcon;
         private View.OnClickListener mListener;
         private boolean mIsEnabled = true;
         private boolean mIsVisible = true;
+        private boolean mIsExpressive = false;
 
         void setUpButton() {
-            mButton.setText(mText);
-            mButton.setOnClickListener(mListener);
-            mButton.setEnabled(mIsEnabled);
-            mButton.setCompoundDrawablesWithIntrinsicBounds(
-                    null /* left */, mIcon /* top */, null /* right */, null /* bottom */);
+            if (mIsExpressive) {
+                mTextView.setText(mText);
+                if (mButton instanceof MaterialButton) {
+                    ((MaterialButton) mButton).setIcon(mIcon);
+                }
+                mButton.setEnabled(mIsEnabled);
+                mActionLayout.setOnClickListener(mListener);
+                mActionLayout.setEnabled(mIsEnabled);
+                mActionLayout.setContentDescription(mText);
+            } else {
+                mButton.setText(mText);
+                mButton.setCompoundDrawablesWithIntrinsicBounds(
+                        null /* left */, mIcon /* top */, null /* right */, null /* bottom */);
+                mButton.setOnClickListener(mListener);
+                mButton.setEnabled(mIsEnabled);
+            }
 
             if (shouldBeVisible()) {
                 mButton.setVisibility(View.VISIBLE);
+                if (mIsExpressive) {
+                    mTextView.setVisibility(View.VISIBLE);
+                    mActionLayout.setVisibility(View.VISIBLE);
+                }
             } else {
                 mButton.setVisibility(View.GONE);
+                if (mIsExpressive) {
+                    mTextView.setVisibility(View.GONE);
+                    mActionLayout.setVisibility(View.GONE);
+                }
             }
         }
 
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 0cb85d8..af07686 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -32,6 +32,8 @@
         "SettingsLibBannerMessagePreference",
         "SettingsLibBarChartPreference",
         "SettingsLibButtonPreference",
+        "SettingsLibBulletPreference",
+        "SettingsLibCardPreference",
         "SettingsLibCollapsingToolbarBaseActivity",
         "SettingsLibDeviceStateRotationLock",
         "SettingsLibDisplayUtils",
@@ -53,6 +55,7 @@
         "SettingsLibTwoTargetPreference",
         "SettingsLibUsageProgressBarPreference",
         "SettingsLibUtils",
+        "SettingsLibZeroStatePreference",
         "settingslib_media_flags_lib",
         "settingslib_flags_lib",
     ],
diff --git a/packages/SettingsLib/BulletPreference/Android.bp b/packages/SettingsLib/BulletPreference/Android.bp
new file mode 100644
index 0000000..3ea0b2b
--- /dev/null
+++ b/packages/SettingsLib/BulletPreference/Android.bp
@@ -0,0 +1,33 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SettingsLibBulletPreference",
+    use_resource_processor: true,
+    defaults: [
+        "SettingsLintDefaults",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.preference_preference",
+        "SettingsLibSettingsTheme",
+    ],
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
diff --git a/packages/SettingsLib/BulletPreference/AndroidManifest.xml b/packages/SettingsLib/BulletPreference/AndroidManifest.xml
new file mode 100644
index 0000000..c7495ef
--- /dev/null
+++ b/packages/SettingsLib/BulletPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget.preference.bullet">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/BulletPreference/res/layout/settingslib_expressive_bullet_icon_frame.xml b/packages/SettingsLib/BulletPreference/res/layout/settingslib_expressive_bullet_icon_frame.xml
new file mode 100644
index 0000000..030f024
--- /dev/null
+++ b/packages/SettingsLib/BulletPreference/res/layout/settingslib_expressive_bullet_icon_frame.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/icon_frame"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minWidth="@dimen/settingslib_expressive_space_medium4"
+    android:gravity="top|start"
+    android:layout_marginTop="@dimen/settingslib_expressive_space_small1"
+    android:paddingStart="@dimen/settingslib_expressive_space_extrasmall4"
+    android:paddingEnd="@dimen/settingslib_expressive_space_small1">
+
+    <androidx.preference.internal.PreferenceImageView
+        android:id="@android:id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:maxWidth="@dimen/settingslib_expressive_space_small4"
+        app:maxHeight="@dimen/settingslib_expressive_space_small4"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/BulletPreference/res/layout/settingslib_expressive_bullet_preference.xml b/packages/SettingsLib/BulletPreference/res/layout/settingslib_expressive_bullet_preference.xml
new file mode 100644
index 0000000..3f37f6c
--- /dev/null
+++ b/packages/SettingsLib/BulletPreference/res/layout/settingslib_expressive_bullet_preference.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:baselineAligned="false">
+
+    <include layout="@layout/settingslib_expressive_bullet_icon_frame"/>
+
+    <include layout="@layout/settingslib_expressive_preference_text_frame"/>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingStart="@dimen/settingslib_expressive_space_small1"
+        android:paddingEnd="0dp"
+        android:orientation="vertical"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/BulletPreference/src/com/android/settingslib/widget/BulletPreference.kt b/packages/SettingsLib/BulletPreference/src/com/android/settingslib/widget/BulletPreference.kt
new file mode 100644
index 0000000..45e2e9a
--- /dev/null
+++ b/packages/SettingsLib/BulletPreference/src/com/android/settingslib/widget/BulletPreference.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.settingslib.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+
+import com.android.settingslib.widget.preference.bullet.R
+
+/**
+ * The BulletPreference shows a text which describe a feature.
+ */
+class BulletPreference @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0
+) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+
+    init {
+        layoutResource = R.layout.settingslib_expressive_bullet_preference
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder) {
+        super.onBindViewHolder(holder)
+        holder.isDividerAllowedAbove = false
+        holder.isDividerAllowedBelow = false
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/CardPreference/Android.bp b/packages/SettingsLib/CardPreference/Android.bp
new file mode 100644
index 0000000..1d871d1
--- /dev/null
+++ b/packages/SettingsLib/CardPreference/Android.bp
@@ -0,0 +1,33 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SettingsLibCardPreference",
+    use_resource_processor: true,
+    defaults: [
+        "SettingsLintDefaults",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.preference_preference",
+        "SettingsLibSettingsTheme",
+    ],
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
diff --git a/packages/SettingsLib/CardPreference/AndroidManifest.xml b/packages/SettingsLib/CardPreference/AndroidManifest.xml
new file mode 100644
index 0000000..717f66e
--- /dev/null
+++ b/packages/SettingsLib/CardPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget.preference.card">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml b/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
new file mode 100644
index 0000000..716ed41
--- /dev/null
+++ b/packages/SettingsLib/CardPreference/res/layout/settingslib_expressive_preference_card.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+<com.google.android.material.card.MaterialCardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/SettingsLibCardStyle">
+
+    <LinearLayout
+        android:id="@+id/card_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:baselineAligned="false"
+        android:minHeight="@dimen/settingslib_expressive_space_large3"
+        android:paddingStart="@dimen/settingslib_expressive_space_small1"
+        android:paddingEnd="@dimen/settingslib_expressive_space_small1"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+
+        <LinearLayout
+            android:id="@+id/icon_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minWidth="@dimen/settingslib_expressive_space_medium3"
+            android:minHeight="@dimen/settingslib_expressive_space_medium3"
+            android:gravity="center"
+            android:orientation="horizontal">
+
+            <ImageView
+                android:id="@android:id/icon"
+                android:src="@drawable/settingslib_arrow_drop_down"
+                android:layout_width="@dimen/settingslib_expressive_space_medium3"
+                android:layout_height="@dimen/settingslib_expressive_space_medium3"
+                android:scaleType="centerInside"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/text_container"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingHorizontal="@dimen/settingslib_expressive_space_small1"
+            android:paddingVertical="@dimen/settingslib_expressive_space_small2"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textAppearance="@style/TextAppearance.CardTitle.SettingsLib"/>
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:textAppearance="@style/TextAppearance.CardSummary.SettingsLib"/>
+
+        </LinearLayout>
+
+        <ImageView
+            android:id="@android:id/closeButton"
+            android:layout_width="@dimen/settingslib_expressive_space_medium4"
+            android:layout_height="@dimen/settingslib_expressive_space_medium4"
+            android:padding="@dimen/settingslib_expressive_space_extrasmall4"
+            android:layout_gravity="center"
+            android:src="@drawable/settingslib_expressive_icon_close"
+            android:background="?android:attr/selectableItemBackground" />
+
+    </LinearLayout>
+
+</com.google.android.material.card.MaterialCardView>
\ No newline at end of file
diff --git a/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml b/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
new file mode 100644
index 0000000..4cbdea5
--- /dev/null
+++ b/packages/SettingsLib/CardPreference/res/values/styles_expressive.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+
+<resources>
+    <style name="TextAppearance.CardTitle.SettingsLib"
+        parent="@style/TextAppearance.PreferenceTitle.SettingsLib">
+        <item name="android:textColor">@color/settingslib_materialColorOnPrimary</item>
+        <item name="android:textSize">20sp</item>
+    </style>
+
+    <style name="TextAppearance.CardSummary.SettingsLib"
+        parent="@style/TextAppearance.PreferenceSummary.SettingsLib">
+        <item name="android:textColor">@color/settingslib_materialColorOnSecondary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
new file mode 100644
index 0000000..eb14746
--- /dev/null
+++ b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 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.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.preference.card.R
+
+/**
+ * The CardPreference shows a card like suggestion in homepage, which also support dismiss.
+ */
+class CardPreference @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0
+) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+
+    init {
+        layoutResource = R.layout.settingslib_expressive_preference_card
+    }
+    private var dismissible = false
+        set(value) {
+            if (field != value) {
+                field = value
+                notifyChanged()
+            }
+        }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder) {
+        super.onBindViewHolder(holder)
+        holder.isDividerAllowedBelow = false
+        holder.isDividerAllowedAbove = false
+
+        holder.findViewById(android.R.id.closeButton)?.let { dismissButton ->
+            dismissButton.visibility = if (dismissible) View.VISIBLE else View.GONE
+            dismissButton.setOnClickListener {
+                isVisible = false
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml
new file mode 100644
index 0000000..ccbe20e
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:start="16dp"
+        android:end="16dp"
+        android:top="4dp"
+        android:bottom="4dp">
+        <shape>
+            <size android:width="32dp" android:height="40dp" />
+            <solid android:color="@color/settingslib_materialColorSurfaceContainerHighest" />
+            <corners
+                android:radius="100dp" />
+        </shape>
+    </item>
+
+    <item
+        android:width="24dp"
+        android:height="24dp"
+        android:gravity="center"
+        android:start="16dp"
+        android:end="16dp"
+        android:top="4dp"
+        android:bottom="4dp">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="960"
+            android:viewportHeight="960"
+            android:tint="@color/settingslib_materialColorOnSurfaceVariant"
+            android:autoMirrored="true">
+            <path
+                android:fillColor="@android:color/white"
+                android:pathData="M313,520L537,744L480,800L160,480L480,160L537,216L313,440L800,440L800,520L313,520Z"/>
+        </vector>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_base_layout.xml
new file mode 100644
index 0000000..b881c57
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_base_layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+    <include layout="@layout/settingslib_expressive_collapsing_toolbar_content_layout"/>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_content_layout.xml
new file mode 100644
index 0000000..6221659
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_content_layout.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/app_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fitsSystemWindows="true"
+        android:outlineAmbientShadowColor="@android:color/transparent"
+        android:outlineSpotShadowColor="@android:color/transparent"
+        android:background="@android:color/transparent"
+        app:expanded="false"
+        android:theme="@style/SettingsLibTheme.CollapsingToolbar.Expressive">
+
+        <com.google.android.material.appbar.CollapsingToolbarLayout
+            android:id="@+id/collapsing_toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/settingslib_toolbar_layout_height"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
+            app:toolbarId="@id/action_bar"
+            style="@style/SettingsLibCollapsingToolbarLayoutStyle.Expressive">
+
+            <Toolbar
+                android:id="@+id/action_bar"
+                android:layout_width="match_parent"
+                android:layout_height="?attr/actionBarSize"
+                android:theme="?android:attr/actionBarTheme"
+                android:transitionName="shared_element_view"
+                app:layout_collapseMode="pin"/>
+
+        </com.google.android.material.appbar.CollapsingToolbarLayout>
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+</merge>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml
new file mode 100644
index 0000000..d58c2c2
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<resources>
+    <style name="SettingsLibCollapsingToolbarLayoutStyle">
+        <item name="expandedTitleTextAppearance">@style/SettingsLibCollapsingToolbarTitle.Expanded</item>
+        <item name="collapsedTitleTextAppearance">@style/SettingsLibCollapsingToolbarTitle.Collapsed</item>
+        <item name="expandedTitleMarginStart">@dimen/settingslib_expressive_space_small4</item>
+        <item name="expandedTitleMarginEnd">@dimen/settingslib_expressive_space_small4</item>
+        <item name="expandedTitleMarginBottom">@dimen/settingslib_expressive_space_medium1</item>
+        <item name="maxLines">3</item>
+        <item name="scrimVisibleHeightTrigger">@dimen/settingslib_scrim_visible_height_trigger</item>
+        <item name="contentScrim">@color/settingslib_materialColorSurfaceVariant</item>
+        <item name="statusBarScrim">@null</item>
+        <item name="scrimAnimationDuration">50</item>
+        <item name="collapsedTitleTextColor">@color/settingslib_materialColorOnSurface</item>
+        <item name="expandedTitleTextColor">@color/settingslib_materialColorOnSurface</item>
+    </style>
+    <style name="SettingsLibCollapsingToolbarLayoutStyle.Expressive">
+        <item name="contentScrim">@color/settingslib_materialColorSurfaceContainer</item>
+    </style>
+
+    <style name="SettingsLibCollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Headline">
+        <!--set dp because we don't want size adjust when font size change-->
+        <item name="android:textSize">20dp</item>
+    </style>
+
+    <style name="SettingsLibCollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed">
+        <item name="android:textSize">36dp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes_expressive.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes_expressive.xml
new file mode 100644
index 0000000..ca1904a
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes_expressive.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+-->
+<resources>
+    <style name="SettingsLibTheme.CollapsingToolbar.Expressive" parent="@style/Theme.MaterialComponents.DayNight"/>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
index f46f110..feacecb 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
@@ -27,6 +27,8 @@
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 
+import com.android.settingslib.widget.SettingsThemeHelper;
+
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.appbar.CollapsingToolbarLayout;
 import com.google.android.material.color.DynamicColors;
@@ -69,7 +71,10 @@
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
             DynamicColors.applyToActivityIfAvailable(this);
         }
-        setTheme(com.android.settingslib.widget.theme.R.style.Theme_SubSettingsBase);
+        int resId = SettingsThemeHelper.isExpressiveTheme(this)
+                ? com.android.settingslib.widget.theme.R.style.Theme_SubSettingsBase_Expressive
+                : com.android.settingslib.widget.theme.R.style.Theme_SubSettingsBase;
+        setTheme(resId);
 
         if (mCustomizeLayoutResId > 0 && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
             super.setContentView(mCustomizeLayoutResId);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 16ed5a8..0f9d94e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -28,6 +28,8 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.settingslib.widget.SettingsThemeHelper;
+
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.appbar.CollapsingToolbarLayout;
 
@@ -59,6 +61,11 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         EdgeToEdgeUtils.enable(this);
         super.onCreate(savedInstanceState);
+
+        if (SettingsThemeHelper.isExpressiveTheme(this)) {
+            setTheme(com.android.settingslib.widget.theme.R.style.Theme_SubSettingsBase_Expressive);
+        }
+
         // for backward compatibility on R devices or wearable devices due to small device size.
         if (mCustomizeLayoutResId > 0 && (Build.VERSION.SDK_INT < Build.VERSION_CODES.S
                 || isWatch())) {
@@ -66,7 +73,7 @@
             return;
         }
 
-        View view = getToolbarDelegate().onCreateView(getLayoutInflater(), null);
+        View view = getToolbarDelegate().onCreateView(getLayoutInflater(), null, this);
         super.setContentView(view);
     }
 
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index 2ab2abd..01ecb66 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -20,6 +20,7 @@
 
 import android.app.ActionBar;
 import android.app.Activity;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
@@ -37,6 +38,8 @@
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 
+import com.android.settingslib.widget.SettingsThemeHelper;
+
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.appbar.CollapsingToolbarLayout;
 
@@ -84,6 +87,8 @@
 
     private boolean mUseCollapsingToolbar;
 
+    private boolean mIsExpressiveTheme;
+
     public CollapsingToolbarDelegate(@NonNull HostCallback hostCallback,
             boolean useCollapsingToolbar) {
         mHostCallback = hostCallback;
@@ -103,11 +108,16 @@
         int layoutId;
         boolean useCollapsingToolbar =
                 mUseCollapsingToolbar || Build.VERSION.SDK_INT < Build.VERSION_CODES.S;
+        Context context = (activity != null) ? activity : inflater.getContext();
+        mIsExpressiveTheme = SettingsThemeHelper.isExpressiveTheme(context);
         if (useCollapsingToolbar) {
-            layoutId = R.layout.collapsing_toolbar_base_layout;
+            layoutId = mIsExpressiveTheme
+                    ? R.layout.settingslib_expressive_collapsing_toolbar_base_layout
+                    : R.layout.collapsing_toolbar_base_layout;
         } else {
             layoutId = R.layout.non_collapsing_toolbar_base_layout;
         }
+
         final View view = inflater.inflate(layoutId, container, false);
         if (view instanceof CoordinatorLayout) {
             mCoordinatorLayout = (CoordinatorLayout) view;
@@ -155,6 +165,9 @@
             if (actionBar != null) {
                 actionBar.setDisplayHomeAsUpEnabled(true);
                 actionBar.setHomeButtonEnabled(true);
+                if (mIsExpressiveTheme) {
+                    actionBar.setHomeAsUpIndicator(R.drawable.settingslib_expressive_icon_back);
+                }
                 actionBar.setDisplayShowTitleEnabled(true);
             }
         }
@@ -174,6 +187,9 @@
         if (actionBar != null) {
             actionBar.setDisplayHomeAsUpEnabled(true);
             actionBar.setHomeButtonEnabled(true);
+            if (mIsExpressiveTheme) {
+                actionBar.setHomeAsUpIndicator(R.drawable.settingslib_expressive_icon_back);
+            }
             actionBar.setDisplayShowTitleEnabled(true);
         }
     }
@@ -188,6 +204,9 @@
         if (actionBar != null) {
             actionBar.setDisplayHomeAsUpEnabled(true);
             actionBar.setHomeButtonEnabled(true);
+            if (mIsExpressiveTheme) {
+                actionBar.setHomeAsUpIndicator(R.drawable.settingslib_expressive_icon_back);
+            }
             actionBar.setDisplayShowTitleEnabled(true);
         }
     }
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index f70add9..51d7504 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -39,6 +39,7 @@
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 
 import com.android.settingslib.collapsingtoolbar.R;
+import com.android.settingslib.widget.SettingsThemeHelper;
 
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -105,7 +106,10 @@
     }
 
     private void init() {
-        inflate(getContext(), R.layout.collapsing_toolbar_content_layout, this);
+        int resId = SettingsThemeHelper.isExpressiveTheme(getContext())
+                ? R.layout.settingslib_expressive_collapsing_toolbar_content_layout
+                : R.layout.collapsing_toolbar_content_layout;
+        inflate(getContext(), resId, this);
         mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
         mAppBarLayout = findViewById(R.id.app_bar);
         if (mCollapsingToolbarLayout != null) {
@@ -172,6 +176,9 @@
             actionBar.setDisplayHomeAsUpEnabled(true);
             actionBar.setHomeButtonEnabled(true);
             actionBar.setDisplayShowTitleEnabled(true);
+            if (SettingsThemeHelper.isExpressiveTheme(getContext())) {
+                actionBar.setHomeAsUpIndicator(R.drawable.settingslib_expressive_icon_back);
+            }
         }
     }
 
@@ -202,6 +209,9 @@
             actionBar.setDisplayHomeAsUpEnabled(true);
             actionBar.setHomeButtonEnabled(true);
             actionBar.setDisplayShowTitleEnabled(true);
+            if (SettingsThemeHelper.isExpressiveTheme(getContext())) {
+                actionBar.setHomeAsUpIndicator(R.drawable.settingslib_expressive_icon_back);
+            }
         }
     }
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable-v35/settingslib_expressive_switch_bar_bg.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v35/settingslib_expressive_switch_bar_bg.xml
new file mode 100644
index 0000000..3999e76
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/drawable-v35/settingslib_expressive_switch_bar_bg.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/settingslib_materialColorPrimaryContainer"/>
+            <corners android:radius="@dimen/settingslib_expressive_radius_extralarge3"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
new file mode 100644
index 0000000..4425ef0
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingVertical="@dimen/settingslib_expressive_space_small1"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/frame"
+        android:minHeight="@dimen/settingslib_expressive_space_large3"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:paddingStart="@dimen/settingslib_expressive_space_medium1"
+        android:paddingEnd="@dimen/settingslib_expressive_space_small3"
+        android:background="@drawable/settingslib_expressive_switch_bar_bg">
+
+        <RelativeLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginEnd="@dimen/settingslib_expressive_space_extrasmall4"
+            android:layout_marginVertical="@dimen/settingslib_expressive_space_small4"
+            android:layout_weight="1">
+
+            <TextView
+                android:id="@+id/switch_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="start"
+                android:ellipsize="end"
+                android:textAlignment="viewStart"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:textColor="@color/settingslib_main_switch_text_color" />
+
+            <TextView
+                android:id="@+id/switch_summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignLeft="@id/switch_text"
+                android:layout_alignStart="@id/switch_text"
+                android:layout_below="@id/switch_text"
+                android:layout_gravity="start"
+                android:textAlignment="viewStart"
+                android:textAppearance="?android:attr/textAppearanceListItemSmall"
+                android:textColor="@color/settingslib_main_switch_text_color"
+                android:visibility="gone" />
+        </RelativeLayout>
+
+        <com.google.android.material.materialswitch.MaterialSwitch
+            android:theme="@style/Theme.Material3.DynamicColors.DayNight"
+            android:id="@android:id/switch_widget"
+            style="@style/SettingslibMainSwitchStyle.Expressive" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index e6f61a8..106802e 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -22,6 +22,7 @@
 import android.os.Build.VERSION_CODES;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -31,6 +32,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.RequiresApi;
 
 import com.android.settingslib.widget.mainswitch.R;
 
@@ -52,6 +54,7 @@
     private int mBackgroundActivatedColor;
 
     protected TextView mTextView;
+    protected TextView mSummaryView;
     protected CompoundButton mSwitch;
     private final View mFrameView;
 
@@ -71,7 +74,11 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
+        boolean isExpressive = SettingsThemeHelper.isExpressiveTheme(context);
+        int resId = isExpressive
+                ? R.layout.settingslib_expressive_main_switch_bar
+                : R.layout.settingslib_main_switch_bar;
+        LayoutInflater.from(context).inflate(resId, this);
 
         if (Build.VERSION.SDK_INT < VERSION_CODES.S) {
             TypedArray a;
@@ -93,6 +100,9 @@
 
         mFrameView = findViewById(R.id.frame);
         mTextView = findViewById(R.id.switch_text);
+        if (isExpressive) {
+            mSummaryView = findViewById(R.id.switch_summary);
+        }
         mSwitch = findViewById(android.R.id.switch_widget);
         addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked));
 
@@ -109,6 +119,12 @@
             final CharSequence title = a.getText(
                     androidx.preference.R.styleable.Preference_android_title);
             setTitle(title);
+            //TODO(b/369470034): update to next version
+            if (isExpressive && Build.VERSION.SDK_INT >= VERSION_CODES.VANILLA_ICE_CREAM) {
+                CharSequence summary = a.getText(
+                        androidx.preference.R.styleable.Preference_android_summary);
+                setSummary(summary);
+            }
             a.recycle();
         }
 
@@ -153,6 +169,18 @@
     }
 
     /**
+     * Set the summary text
+     */
+    @RequiresApi(VERSION_CODES.VANILLA_ICE_CREAM)
+    //TODO(b/369470034): update to next version
+    public void setSummary(CharSequence text) {
+        if (mSummaryView != null) {
+            mSummaryView.setText(text);
+            mSummaryView.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
+        }
+    }
+
+    /**
      * Set icon space reserved for title
      */
     public void setIconSpaceReserved(boolean iconSpaceReserved) {
@@ -219,6 +247,12 @@
             mFrameView.setEnabled(enabled);
             mFrameView.setActivated(isChecked());
         }
+
+        if (SettingsThemeHelper.isExpressiveTheme(getContext())) {
+            if (mSummaryView != null) {
+                mSummaryView.setEnabled(enabled);
+            }
+        }
     }
 
     private void propagateChecked(boolean isChecked) {
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index b294d4e..d895c87 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -89,6 +90,10 @@
                     androidx.preference.R.styleable.Preference_android_title);
             setTitle(title);
 
+            CharSequence summary = a.getText(
+                    androidx.preference.R.styleable.Preference_android_summary);
+            setSummary(summary);
+
             final boolean bIconSpaceReserved = a.getBoolean(
                     androidx.preference.R.styleable.Preference_android_iconSpaceReserved, true);
             setIconSpaceReserved(bIconSpaceReserved);
@@ -113,6 +118,15 @@
     }
 
     @Override
+    public void setSummary(CharSequence summary) {
+        super.setSummary(summary);
+        if (mMainSwitchBar != null
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            mMainSwitchBar.setSummary(summary);
+        }
+    }
+
+    @Override
     public void setIconSpaceReserved(boolean iconSpaceReserved) {
         super.setIconSpaceReserved(iconSpaceReserved);
         if (mMainSwitchBar != null) {
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 4503738..6992005 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -54,6 +54,20 @@
         children.add(PreferenceHierarchyNode(metadata))
     }
 
+    /** Adds a preference to the hierarchy before a specific preference. */
+    fun addBefore(key: String, metadata: PreferenceMetadata) {
+        var foundIndex = children.indexOfFirst { it.metadata.key == key }
+        if (foundIndex == -1) foundIndex = children.size
+        children.add(foundIndex, PreferenceHierarchyNode(metadata))
+    }
+
+    /** Adds a preference to the hierarchy after a specific preference. */
+    fun addAfter(key: String, metadata: PreferenceMetadata) {
+        var foundIndex = children.indexOfFirst { it.metadata.key == key }
+        if (foundIndex == -1) foundIndex = children.size else foundIndex++
+        children.add(foundIndex, PreferenceHierarchyNode(metadata))
+    }
+
     /** Adds a preference group to the hierarchy. */
     operator fun PreferenceGroup.unaryPlus() = PreferenceHierarchy(this).also { children.add(it) }
 
diff --git a/packages/SettingsLib/Service/Android.bp b/packages/SettingsLib/Service/Android.bp
new file mode 100644
index 0000000..65d0e4c
--- /dev/null
+++ b/packages/SettingsLib/Service/Android.bp
@@ -0,0 +1,20 @@
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "SettingsLibService-srcs",
+    srcs: ["src/**/*.kt"],
+}
+
+android_library {
+    name: "SettingsLibService",
+    defaults: [
+        "SettingsLintDefaults",
+    ],
+    srcs: [":SettingsLibService-srcs"],
+    static_libs: [
+        "SettingsLibGraph",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SettingsLib/Service/AndroidManifest.xml b/packages/SettingsLib/Service/AndroidManifest.xml
new file mode 100644
index 0000000..56d7818
--- /dev/null
+++ b/packages/SettingsLib/Service/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.settingslib.service">
+
+  <uses-sdk android:minSdkVersion="21" />
+</manifest>
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
new file mode 100644
index 0000000..95661c9
--- /dev/null
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.settingslib.service
+
+import android.app.Application
+import com.android.settingslib.graph.GetPreferenceGraphApiHandler
+import com.android.settingslib.graph.GetPreferenceGraphRequest
+
+/** Api to get preference graph. */
+internal class PreferenceGraphApi(activityClasses: Set<String>) :
+    GetPreferenceGraphApiHandler(activityClasses) {
+    override val id: Int
+        get() = API_GET_PREFERENCE_GRAPH
+
+    override fun hasPermission(
+        application: Application,
+        myUid: Int,
+        callingUid: Int,
+        request: GetPreferenceGraphRequest,
+    ): Boolean {
+        return true // TODO: add permission check
+    }
+}
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
new file mode 100644
index 0000000..d382dad
--- /dev/null
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.settingslib.service
+
+import com.android.settingslib.ipc.ApiHandler
+import com.android.settingslib.ipc.MessengerService
+import com.android.settingslib.ipc.PermissionChecker
+
+/**
+ * Preference service providing a bunch of APIs.
+ *
+ * In AndroidManifest.xml, the <service> must specify <intent-filter> with action
+ * [PREFERENCE_SERVICE_ACTION].
+ */
+open class PreferenceService(
+    permissionChecker: PermissionChecker,
+    name: String = "PreferenceService",
+) :
+    MessengerService(
+        listOf<ApiHandler<*, *>>(PreferenceGraphApi(setOf())),
+        permissionChecker,
+        name,
+    )
diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
new file mode 100644
index 0000000..8f03111
--- /dev/null
+++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt
@@ -0,0 +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.
+ */
+
+package com.android.settingslib.service
+
+const val PREFERENCE_SERVICE_ACTION = "com.android.settingslib.PREFERENCE_SERVICE"
+
+internal const val API_GET_PREFERENCE_GRAPH = 1
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index 996477c..1661dfb 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -10,8 +10,15 @@
 android_library {
     name: "SettingsLibSettingsTheme",
     use_resource_processor: true,
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     resource_dirs: ["res"],
-    static_libs: ["androidx.preference_preference"],
+    static_libs: [
+        "androidx.preference_preference",
+        "com.google.android.material_material",
+    ],
     sdk_version: "system_current",
     min_sdk_version: "21",
     apex_available: [
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_expressive_color_main_switch_track.xml b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_expressive_color_main_switch_track.xml
new file mode 100644
index 0000000..a6885a4
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_expressive_color_main_switch_track.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- State Disabled, Unchecked -->
+    <item
+        android:alpha="?android:attr/disabledAlpha"
+        android:color="@color/settingslib_materialColorSurfaceContainerHighest"
+        android:state_enabled="false" android:state_checked="false"/>
+    <!-- State Disabled, Checked -->
+    <item
+        android:alpha="?android:attr/disabledAlpha"
+        android:color="@color/settingslib_materialColorPrimary"
+        android:state_enabled="false" android:state_checked="true"/>
+    <!-- State Checked -->
+    <item
+        android:color="@color/settingslib_materialColorPrimary"
+        android:state_checked="true"/>
+
+    <!-- State Unchecked -->
+    <item
+        android:color="@color/settingslib_materialColorSurfaceContainerHighest"
+        android:state_hovered="true" android:state_checked="false"/>
+    <item
+        android:color="@color/settingslib_materialColorSurfaceContainerHighest"
+        android:state_focused="true" android:state_checked="false"/>
+    <item
+        android:color="@color/settingslib_materialColorSurfaceContainerHighest"
+        android:state_pressed="true" android:state_checked="false"/>
+    <item
+        android:color="@color/settingslib_materialColorSurfaceContainerHighest"
+        android:state_checked="false"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_check.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_check.xml
new file mode 100644
index 0000000..309dbdf
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_check.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M382,720L154,492L211,435L382,606L749,239L806,296L382,720Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_chevron.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_chevron.xml
new file mode 100644
index 0000000..16ca18a
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_chevron.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="18dp"
+    android:height="18dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="@color/settingslib_materialColorOnSurfaceVariant"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M321,880L250,809L579,480L250,151L321,80L721,480L321,880Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_close.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_close.xml
new file mode 100644
index 0000000..e6df8a4
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_close.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M256,760L200,704L424,480L200,256L256,200L480,424L704,200L760,256L536,480L760,704L704,760L480,536L256,760Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_collapse.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_collapse.xml
new file mode 100644
index 0000000..161ece7
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_collapse.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape
+            android:shape="oval">
+            <size android:width="24dp" android:height="24dp"/>
+            <solid android:color="@color/settingslib_materialColorSurfaceDim"/>
+        </shape>
+    </item>
+    <item>
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="960"
+            android:viewportHeight="960">
+            <path
+                android:fillColor="@color/settingslib_materialColorOnSurface"
+                android:pathData="M480,432L296,616L240,560L480,320L720,560L664,616L480,432Z"/>
+        </vector>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_expand.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_expand.xml
new file mode 100644
index 0000000..1b5d518
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_icon_expand.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape
+            android:shape="oval">
+            <size android:width="24dp" android:height="24dp"/>
+            <solid android:color="@color/settingslib_materialColorSurfaceDim"/>
+        </shape>
+    </item>
+    <item>
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="960"
+            android:viewportHeight="960">
+            <path
+                android:fillColor="@color/settingslib_materialColorOnSurface"
+                android:pathData="M480,616L240,376L296,320L480,504L664,320L720,376L480,616Z"/>
+        </vector>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_switch_thumb_icon.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_switch_thumb_icon.xml
new file mode 100644
index 0000000..342729d
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_expressive_switch_thumb_icon.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:drawable="@drawable/settingslib_expressive_icon_check"/>
+    <item android:state_checked="false" android:drawable="@drawable/settingslib_expressive_icon_close"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
index f4766ee..543b237 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
@@ -26,7 +26,10 @@
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
+                android:topLeftRadius="4dp"
+                android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
+                android:topRightRadius="4dp"
+                android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
             <padding
                 android:bottom="16dp"/>
         </shape>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
index 40eafc2..6d2cd1a 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
@@ -25,7 +25,7 @@
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
+                android:radius="4dp" />
         </shape>
     </item>
 </ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
index f4766ee..bcdbf1d 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
@@ -26,7 +26,7 @@
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
+                android:radius="@dimen/settingslib_preference_corner_radius" />
             <padding
                 android:bottom="16dp"/>
         </shape>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
index 40eafc2..d4b658c 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
@@ -25,7 +25,10 @@
             <solid
                 android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
+                android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
+                android:bottomLeftRadius="4dp"
+                android:topRightRadius="@dimen/settingslib_preference_corner_radius"
+                android:bottomRightRadius="4dp" />
         </shape>
     </item>
 </ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_bullet_start.xml b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_bullet_start.xml
new file mode 100644
index 0000000..9216c96
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_expressive_icon_bullet_start.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M354,673L480,597L606,674L573,530L684,434L538,421L480,285L422,420L276,433L387,530L354,673ZM233,840L298,559L80,370L368,345L480,80L592,345L880,370L662,559L727,840L480,691L233,840ZM480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index dda7517c..952562e 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -31,37 +31,7 @@
 
     <include layout="@layout/settingslib_icon_frame"/>
 
-    <RelativeLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp">
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="start"
-            android:textAlignment="viewStart"
-            android:maxLines="2"
-            android:textAppearance="?android:attr/textAppearanceListItem"
-            android:ellipsize="marquee"/>
-
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignLeft="@android:id/title"
-            android:layout_alignStart="@android:id/title"
-            android:layout_gravity="start"
-            android:textAlignment="viewStart"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="10"
-            style="@style/PreferenceSummaryTextStyle"/>
-
-    </RelativeLayout>
+    <include layout="@layout/settingslib_preference_frame"/>
 
     <!-- Preference should place its actual preference widget here. -->
     <LinearLayout
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference_frame.xml
new file mode 100644
index 0000000..433d264
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference_frame.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:paddingTop="16dp"
+    android:paddingBottom="16dp">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
+        android:text="Title"
+        android:maxLines="2"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:ellipsize="marquee"/>
+
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_alignLeft="@android:id/title"
+        android:layout_alignStart="@android:id/title"
+        android:text="Summary summary summary"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
+        android:textColor="?android:attr/textColorSecondary"
+        android:maxLines="10"
+        style="@style/PreferenceSummaryTextStyle"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
index fedcc77..4e23b65 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -31,41 +31,7 @@
 
     <include layout="@layout/settingslib_icon_frame"/>
 
-    <RelativeLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp">
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="start"
-            android:textAlignment="viewStart"
-            android:maxLines="2"
-            android:hyphenationFrequency="normalFast"
-            android:lineBreakWordStyle="phrase"
-            android:textAppearance="?android:attr/textAppearanceListItem"
-            android:ellipsize="marquee"/>
-
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignLeft="@android:id/title"
-            android:layout_alignStart="@android:id/title"
-            android:layout_gravity="start"
-            android:textAlignment="viewStart"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="10"
-            android:hyphenationFrequency="normalFast"
-            android:lineBreakWordStyle="phrase"
-            style="@style/PreferenceSummaryTextStyle"/>
-
-    </RelativeLayout>
+    <include layout="@layout/settingslib_preference_frame"/>
 
     <!-- Preference should place its actual preference widget here. -->
     <LinearLayout
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference_frame.xml
new file mode 100644
index 0000000..f93e1b9
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference_frame.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:paddingTop="16dp"
+    android:paddingBottom="16dp">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
+        android:text="Title"
+        android:maxLines="2"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:ellipsize="marquee"/>
+
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_alignLeft="@android:id/title"
+        android:layout_alignStart="@android:id/title"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
+        android:textColor="?android:attr/textColorSecondary"
+        android:maxLines="10"
+        android:text="Summary summary summary"
+        android:hyphenationFrequency="normalFast"
+        android:lineBreakWordStyle="phrase"
+        style="@style/PreferenceSummaryTextStyle"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
new file mode 100644
index 0000000..245d368
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_collapsable_textview.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="@dimen/settingslib_expressive_space_small1"
+    android:paddingTop="@dimen/settingslib_expressive_space_extrasmall4"
+    android:orientation="vertical"
+    android:animateLayoutChanges="true"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:textAlignment="viewStart"
+        android:clickable="false"
+        android:longClickable="false"
+        android:maxLines="10"
+        android:ellipsize="end"
+        android:textAppearance="@style/TextAppearance.TopIntroText"/>
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/collapse_button"
+        app:layout_constraintTop_toBottomOf="@android:id/title"
+        app:layout_constraintStart_toStartOf="parent"
+        android:text="@string/settingslib_expressive_text_expand"
+        app:icon="@drawable/settingslib_expressive_icon_expand"
+        style="@style/SettingslibTextButtonStyle.Expressive"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml
new file mode 100644
index 0000000..2475dfd
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false"
+    android:baselineAligned="false">
+
+    <include layout="@layout/settingslib_expressive_preference_icon_frame"/>
+
+    <include layout="@layout/settingslib_expressive_preference_text_frame"/>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingStart="@dimen/settingslib_expressive_space_small1"
+        android:paddingEnd="0dp"
+        android:orientation="vertical"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
new file mode 100644
index 0000000..f5017a5
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/icon_frame"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:minWidth="@dimen/settingslib_expressive_space_medium3"
+    android:minHeight="@dimen/settingslib_expressive_space_medium3"
+    android:gravity="center"
+    android:layout_marginEnd="-8dp">
+
+    <androidx.preference.internal.PreferenceImageView
+        android:id="@android:id/icon"
+        android:layout_width="@dimen/settingslib_expressive_space_medium3"
+        android:layout_height="@dimen/settingslib_expressive_space_medium3"
+        android:scaleType="centerInside"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml
new file mode 100644
index 0000000..4cbdfd5
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<com.google.android.material.materialswitch.MaterialSwitch
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:theme="@style/Theme.Material3.DynamicColors.DayNight"
+    android:id="@+id/switchWidget"
+    style="@style/SettingslibSwitchStyle.Expressive"/>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
new file mode 100644
index 0000000..e3e689b
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/settingslib_expressive_space_none"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:padding="@dimen/settingslib_expressive_space_small1">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:maxLines="2"
+        android:ellipsize="marquee"/>
+
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_alignLeft="@android:id/title"
+        android:layout_alignStart="@android:id/title"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
+        android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+        android:textColor="?android:attr/textColorSecondary"
+        android:maxLines="10"/>
+</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml
new file mode 100644
index 0000000..3f75181
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/two_target_divider"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:gravity="start|center_vertical"
+    android:orientation="horizontal"
+    android:paddingStart="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall7">
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingEnd="@dimen/settingslib_expressive_space_extrasmall6"
+        android:src="@drawable/settingslib_expressive_icon_chevron"/>
+
+    <View
+        android:layout_width="1dp"
+        android:layout_height="40dp"
+        android:background="?android:attr/listDivider" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/attrs_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/attrs_expressive.xml
new file mode 100644
index 0000000..857dd79
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/attrs_expressive.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <declare-styleable name="CollapsableTextView">
+        <attr name="android:gravity"/>
+        <!-- The minimum number of lines when the textView collapsed. -->
+        <attr name="android:minLines"/>
+        <!-- Specifies that the textView is collapsable. -->
+        <attr name="isCollapsable" format="boolean"/>
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens_expressive.xml
new file mode 100644
index 0000000..0542c51
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens_expressive.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<resources>
+    <!-- Expressive design start -->
+    <!-- corner radius token -->
+    <dimen name="settingslib_expressive_radius_none">0dp</dimen>
+    <dimen name="settingslib_expressive_radius_full">360dp</dimen>
+    <dimen name="settingslib_expressive_radius_extrasmall1">2dp</dimen>
+    <dimen name="settingslib_expressive_radius_extrasmall2">4dp</dimen>
+    <dimen name="settingslib_expressive_radius_small">8dp</dimen>
+    <dimen name="settingslib_expressive_radius_medium">12dp</dimen>
+    <dimen name="settingslib_expressive_radius_large1">16dp</dimen>
+    <dimen name="settingslib_expressive_radius_large2">20dp</dimen>
+    <dimen name="settingslib_expressive_radius_large3">24dp</dimen>
+    <dimen name="settingslib_expressive_radius_extralarge1">28dp</dimen>
+    <dimen name="settingslib_expressive_radius_extralarge2">32dp</dimen>
+    <dimen name="settingslib_expressive_radius_extralarge3">42dp</dimen>
+
+    <!-- space token -->
+    <dimen name="settingslib_expressive_space_none">0dp</dimen>
+    <dimen name="settingslib_expressive_space_extrasmall1">2dp</dimen>
+    <dimen name="settingslib_expressive_space_extrasmall2">4dp</dimen>
+    <dimen name="settingslib_expressive_space_extrasmall3">6dp</dimen>
+    <dimen name="settingslib_expressive_space_extrasmall4">8dp</dimen>
+    <dimen name="settingslib_expressive_space_extrasmall5">10dp</dimen>
+    <dimen name="settingslib_expressive_space_extrasmall6">12dp</dimen>
+    <dimen name="settingslib_expressive_space_extrasmall7">14dp</dimen>
+    <dimen name="settingslib_expressive_space_small1">16dp</dimen>
+    <dimen name="settingslib_expressive_space_small2">18dp</dimen>
+    <dimen name="settingslib_expressive_space_small3">20dp</dimen>
+    <dimen name="settingslib_expressive_space_small4">24dp</dimen>
+    <dimen name="settingslib_expressive_space_medium1">32dp</dimen>
+    <dimen name="settingslib_expressive_space_medium2">36dp</dimen>
+    <dimen name="settingslib_expressive_space_medium3">40dp</dimen>
+    <dimen name="settingslib_expressive_space_medium4">48dp</dimen>
+    <dimen name="settingslib_expressive_space_medium5">56dp</dimen>
+    <dimen name="settingslib_expressive_space_large1">60dp</dimen>
+    <dimen name="settingslib_expressive_space_large2">64dp</dimen>
+    <dimen name="settingslib_expressive_space_large3">72dp</dimen>
+    <dimen name="settingslib_expressive_space_large4">80dp</dimen>
+    <dimen name="settingslib_expressive_space_large5">96dp</dimen>
+    <!-- Expressive theme end -->
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/strings.xml
new file mode 100644
index 0000000..2273406
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- text of button to indicate user the textView is expandable [CHAR LIMIT=NONE] -->
+    <string name="settingslib_expressive_text_expand">Expand</string>
+    <!-- text of button to indicate user the textView is collapsable [CHAR LIMIT=NONE] -->
+    <string name="settingslib_expressive_text_collapse">Collapse</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml
new file mode 100644
index 0000000..250c27e
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml
@@ -0,0 +1,293 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<resources>
+    <style name="SettingsLibTextAppearance" parent="@android:style/TextAppearance.DeviceDefault">
+        <!--item name="android:fontFamily"></item-->
+        <item name="android:hyphenationFrequency">normalFast</item>
+        <item name="android:lineBreakWordStyle">phrase</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Primary">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+
+    <style name="SettingsLibTextAppearance.Primary.Display">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Display.Large">
+        <item name="android:textSize">57sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Display.Medium">
+        <item name="android:textSize">45sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Display.Small">
+        <item name="android:textSize">36sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Primary.Headline">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Headline.Large">
+        <item name="android:textSize">32sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Headline.Medium">
+        <item name="android:textSize">28sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Headline.Small">
+        <item name="android:textSize">24sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Primary.Title">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Title.Large">
+        <item name="android:textSize">22sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Title.Medium">
+        <item name="android:textSize">16sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Title.Small">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Primary.Label">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Label.Large">
+        <item name="android:textSize">14sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Label.Medium">
+        <item name="android:textSize">12sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Label.Small">
+        <item name="android:textSize">11sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Primary.Body">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Body.Large">
+        <item name="android:textSize">16sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Body.Medium">
+        <item name="android:textSize">14sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Primary.Body.Small">
+        <item name="android:textSize">12sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Emphasized">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+
+    <style name="SettingsLibTextAppearance.Emphasized.Display">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Display.Large">
+        <item name="android:textSize">57sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Display.Medium">
+        <item name="android:textSize">45sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Display.Small">
+        <item name="android:textSize">36sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Emphasized.Headline">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Headline.Large">
+        <item name="android:textSize">32sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Headline.Medium">
+        <item name="android:textSize">28sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Headline.Small">
+        <item name="android:textSize">24sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Emphasized.Title">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Title.Large">
+        <item name="android:textSize">22sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Title.Medium">
+        <item name="android:textSize">16sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Title.Small">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Emphasized.Label">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Label.Large">
+        <item name="android:textSize">14sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Label.Medium">
+        <item name="android:textSize">12sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Label.Small">
+        <item name="android:textSize">11sp</item>
+    </style>
+
+    <style name="SettingsLibTextAppearance.Emphasized.Body">
+        <!--item name="android:fontFamily"></item-->
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Body.Large">
+        <item name="android:textSize">16sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Body.Medium">
+        <item name="android:textSize">14sp</item>
+    </style>
+    <style name="SettingsLibTextAppearance.Emphasized.Body.Small">
+        <item name="android:textSize">12sp</item>
+    </style>
+
+    <style name="SettingslibSwitchStyle.Expressive" parent="">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
+        <item name="android:background">@null</item>
+        <item name="android:clickable">false</item>
+        <item name="android:focusable">false</item>
+        <item name="thumbIcon">@drawable/settingslib_expressive_switch_thumb_icon</item>
+    </style>
+
+    <style name="SettingslibMainSwitchStyle.Expressive" parent="SettingslibSwitchStyle.Expressive">
+        <item name="android:layout_gravity">center</item>
+        <item name="trackTint">@color/settingslib_expressive_color_main_switch_track</item>
+    </style>
+
+    <style name="SettingsLibCardStyle" parent="">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginHorizontal">?android:attr/listPreferredItemPaddingStart</item>
+        <item name="android:layout_marginVertical">@dimen/settingslib_expressive_space_extrasmall4</item>
+        <item name="cardBackgroundColor">@color/settingslib_materialColorPrimary</item>
+        <item name="cardCornerRadius">@dimen/settingslib_expressive_radius_extralarge3</item>
+        <item name="cardElevation">0dp</item>
+        <item name="rippleColor">?android:attr/colorControlHighlight</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Filled"
+        parent="@style/Widget.Material3.Button">
+        <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
+        <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
+        <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
+        <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
+        <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:backgroundTint">@color/settingslib_materialColorPrimary</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textColor">@color/settingslib_materialColorOnPrimary</item>
+        <item name="android:textSize">14sp</item>
+        <item name="iconGravity">textStart</item>
+        <item name="iconTint">@color/settingslib_materialColorOnPrimary</item>
+        <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Filled.Large">
+        <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Filled.Extra"
+        parent="@style/SettingsLibButtonStyle.Expressive.Filled.Large">
+        <item name="android:layout_width">match_parent</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Tonal"
+        parent="@style/Widget.Material3.Button.TonalButton">
+        <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
+        <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
+        <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
+        <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
+        <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:backgroundTint">@color/settingslib_materialColorSecondaryContainer</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textColor">@color/settingslib_materialColorOnSecondaryContainer</item>
+        <item name="android:textSize">14sp</item>
+        <item name="iconGravity">textStart</item>
+        <item name="iconTint">@color/settingslib_materialColorOnSecondaryContainer</item>
+        <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Tonal.Large">
+        <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Tonal.Extra"
+        parent="@style/SettingsLibButtonStyle.Expressive.Tonal.Large">
+        <item name="android:layout_width">match_parent</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Outline"
+        parent="@style/Widget.Material3.Button.OutlinedButton.Icon">
+        <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
+        <item name="android:minWidth">@dimen/settingslib_expressive_space_medium4</item>
+        <item name="android:minHeight">@dimen/settingslib_expressive_space_medium4</item>
+        <item name="android:paddingVertical">@dimen/settingslib_expressive_space_extrasmall5</item>
+        <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textColor">@color/settingslib_materialColorPrimary</item>
+        <item name="android:textSize">14sp</item>
+        <item name="iconTint">@color/settingslib_materialColorPrimary</item>
+        <item name="iconGravity">textStart</item>
+        <item name="iconSize">@dimen/settingslib_expressive_space_small4</item>
+        <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item>
+        <item name="strokeColor">@color/settingslib_materialColorOutlineVariant</item>
+
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Outline.Large">
+        <item name="android:paddingVertical">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:paddingHorizontal">@dimen/settingslib_expressive_space_small4</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="SettingsLibButtonStyle.Expressive.Outline.Extra"
+        parent="@style/SettingsLibButtonStyle.Expressive.Outline.Large">
+        <item name="android:layout_width">match_parent</item>
+    </style>
+
+    <style name="SettingslibTextButtonStyle.Expressive"
+        parent="@style/Widget.Material3.Button.TextButton.Icon">
+        <item name="android:theme">@style/Theme.Material3.DynamicColors.DayNight</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/settingslib_materialColorOnSurface</item>
+        <item name="iconTint">@null</item>
+        <item name="iconPadding">@dimen/settingslib_expressive_space_extrasmall4</item>
+        <item name="rippleColor">?android:attr/colorControlHighlight</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml
new file mode 100644
index 0000000..3c69027
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+<resources>
+    <style name="SettingsLibPreference" parent="SettingsPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.Category" parent="SettingsCategoryPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.CheckBoxPreference" parent="SettingsCheckBoxPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.SwitchPreferenceCompat" parent="SettingsSwitchPreferenceCompat.SettingsLib"/>
+
+    <style name="SettingsLibPreference.SeekBarPreference" parent="SettingsSeekbarPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.PreferenceScreen" parent="SettingsPreferenceScreen.SettingsLib"/>
+
+    <style name="SettingsLibPreference.DialogPreference" parent="SettingsPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.DialogPreference.EditTextPreference" parent="SettingsEditTextPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.DropDown" parent="SettingsDropdownPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.SwitchPreference" parent="SettingsSwitchPreference.SettingsLib"/>
+
+    <style name="SettingsLibPreference.Expressive">
+        <item name="android:layout">@layout/settingslib_expressive_preference</item>
+    </style>
+
+    <style name="SettingsLibPreference.Category.Expressive">
+    </style>
+
+    <style name="SettingsLibPreference.CheckBoxPreference.Expressive">
+        <item name="android:layout">@layout/settingslib_expressive_preference</item>
+    </style>
+
+    <style name="SettingsLibPreference.SwitchPreferenceCompat.Expressive">
+        <item name="android:layout">@layout/settingslib_expressive_preference</item>
+        <item name="android:widgetLayout">@layout/settingslib_expressive_preference_switch</item>
+    </style>
+
+    <style name="SettingsLibPreference.SeekBarPreference.Expressive"/>
+
+    <style name="SettingsLibPreference.PreferenceScreen.Expressive">
+        <item name="android:layout">@layout/settingslib_expressive_preference</item>
+    </style>
+
+    <style name="SettingsLibPreference.DialogPreference.Expressive">
+    </style>
+
+    <style name="SettingsLibPreference.DialogPreference.EditTextPreference.Expressive">
+        <item name="android:layout">@layout/settingslib_expressive_preference</item>
+        <item name="android:dialogLayout">@layout/settingslib_preference_dialog_edittext</item>
+    </style>
+
+    <style name="SettingsLibPreference.DropDown.Expressive">
+    </style>
+
+    <style name="SettingsLibPreference.SwitchPreference.Expressive"/>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml
new file mode 100644
index 0000000..fea8739
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<resources>
+    <style name="Theme.SettingsBase.Expressive">
+        <!-- Set up Preference title text style -->
+        <!--item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item-->
+        <!--item name="android:textAppearanceListItemSecondary">@style/textAppearanceListItemSecondary</item-->
+
+        <!-- Set up  list item padding -->
+        <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:listPreferredItemPaddingEnd">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:listPreferredItemPaddingRight">@dimen/settingslib_expressive_space_small1</item>
+        <item name="android:listPreferredItemHeightSmall">@dimen/settingslib_expressive_space_large3</item>
+
+        <!-- Set up preference theme -->
+        <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib.Expressive</item>
+
+        <!-- Set up Spinner style -->
+        <!--item name="android:spinnerStyle"></item>
+        <item name="android:spinnerItemStyle"></item>
+        <item name="android:spinnerDropDownItemStyle"></item-->
+
+        <!-- Set up edge-to-edge configuration for top app bar -->
+        <item name="android:clipToPadding">false</item>
+        <item name="android:clipChildren">false</item>
+    </style>
+
+    <!-- Using in SubSettings page including injected settings page -->
+    <style name="Theme.SubSettingsBase.Expressive" parent="Theme.SettingsBase.Expressive">
+        <!-- Suppress the built-in action bar -->
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+
+        <!-- Set up edge-to-edge configuration for top app bar -->
+        <item name="android:navigationBarColor">@android:color/transparent</item>
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="colorControlNormal">?android:attr/colorControlNormal</item>
+
+        <!-- For AndroidX AlertDialog -->
+        <!--item name="alertDialogTheme">@style/Theme.AlertDialog.SettingsLib</item-->
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/themes_preference_expressive.xml
new file mode 100644
index 0000000..41fe225
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/themes_preference_expressive.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+  -->
+
+<resources>
+    <style name="PreferenceTheme.SettingsLib.Expressive">
+        <item name="checkBoxPreferenceStyle">@style/SettingsLibPreference.CheckBoxPreference.Expressive</item>
+        <item name="dialogPreferenceStyle">@style/SettingsLibPreference.DialogPreference.Expressive</item>
+        <item name="dropdownPreferenceStyle">@style/SettingsLibPreference.DropDown.Expressive</item>
+        <item name="editTextPreferenceStyle">@style/SettingsLibPreference.DialogPreference.EditTextPreference.Expressive</item>
+        <item name="seekBarPreferenceStyle">@style/SettingsLibPreference.SeekBarPreference.Expressive</item>
+        <item name="preferenceCategoryStyle">@style/SettingsLibPreference.Category.Expressive</item>
+        <item name="preferenceScreenStyle">@style/SettingsLibPreference.PreferenceScreen.Expressive</item>
+        <item name="preferenceStyle">@style/SettingsLibPreference.Expressive</item>
+        <item name="switchPreferenceCompatStyle">@style/SettingsLibPreference.SwitchPreferenceCompat.Expressive</item>
+        <item name="preferenceCategoryTitleTextAppearance">@style/TextAppearance.CategoryTitle.SettingsLib</item>
+        <item name="preferenceCategoryTitleTextColor">@color/settingslib_materialColorPrimary</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
new file mode 100644
index 0000000..127f21a
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
@@ -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.settingslib.widget
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.settingslib.widget.theme.R
+import com.google.android.material.button.MaterialButton
+
+class CollapsableTextView @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private var isCollapsable: Boolean = false
+    private var isCollapsed: Boolean = false
+    private var minLines: Int = DEFAULT_MIN_LINES
+
+    private val titleTextView: TextView
+    private val collapseButton: MaterialButton
+    private val collapseButtonResources: CollapseButtonResources
+
+    init {
+        LayoutInflater.from(context)
+            .inflate(R.layout.settingslib_expressive_collapsable_textview, this)
+        titleTextView = findViewById(android.R.id.title)
+        collapseButton = findViewById(R.id.collapse_button)
+
+        collapseButtonResources = CollapseButtonResources(
+            context.getDrawable(R.drawable.settingslib_expressive_icon_collapse)!!,
+            context.getDrawable(R.drawable.settingslib_expressive_icon_expand)!!,
+            context.getString(R.string.settingslib_expressive_text_collapse),
+            context.getString(R.string.settingslib_expressive_text_expand)
+        )
+
+        collapseButton.setOnClickListener {
+            isCollapsed = !isCollapsed
+            updateView()
+        }
+
+        initAttributes(context, attrs, defStyleAttr)
+    }
+
+    private fun initAttributes(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
+        context.obtainStyledAttributes(
+            attrs, Attrs, defStyleAttr, 0
+        ).apply {
+            val gravity = getInt(GravityAttr, Gravity.START)
+            when (gravity) {
+                Gravity.CENTER_VERTICAL, Gravity.CENTER, Gravity.CENTER_HORIZONTAL -> {
+                    centerHorizontally(titleTextView)
+                    centerHorizontally(collapseButton)
+                }
+            }
+            recycle()
+        }
+    }
+
+    private fun centerHorizontally(view: View) {
+        (view.layoutParams as LayoutParams).apply {
+            startToStart = LayoutParams.PARENT_ID
+            endToEnd = LayoutParams.PARENT_ID
+            horizontalBias = 0.5f
+        }
+    }
+
+    /**
+     * Sets the text content of the CollapsableTextView.
+     * @param text The text to display.
+     */
+    fun setText(text: String) {
+        titleTextView.text = text
+    }
+
+    /**
+     * Sets whether the text view is collapsable.
+     * @param collapsable True if the text view should be collapsable, false otherwise.
+     */
+    fun setCollapsable(collapsable: Boolean) {
+        isCollapsable = collapsable
+        updateView()
+    }
+
+    /**
+     * Sets the minimum number of lines to display when collapsed.
+     * @param lines The minimum number of lines.
+     */
+    fun setMinLines(line: Int) {
+        minLines = line.coerceIn(1, DEFAULT_MAX_LINES)
+        updateView()
+    }
+
+    private fun updateView() {
+        when {
+            isCollapsed -> {
+                collapseButton.apply {
+                    text = collapseButtonResources.expandText
+                    icon = collapseButtonResources.expandIcon
+                }
+                titleTextView.maxLines = minLines
+            }
+
+            else -> {
+                collapseButton.apply {
+                    text = collapseButtonResources.collapseText
+                    icon = collapseButtonResources.collapseIcon
+                }
+                titleTextView.maxLines = DEFAULT_MAX_LINES
+            }
+        }
+        collapseButton.visibility = if (isCollapsable) VISIBLE else GONE
+    }
+
+    private data class CollapseButtonResources(
+        val collapseIcon: Drawable,
+        val expandIcon: Drawable,
+        val collapseText: String,
+        val expandText: String
+    )
+
+    companion object {
+        private const val DEFAULT_MAX_LINES = 10
+        private const val DEFAULT_MIN_LINES = 2
+
+        private val Attrs = R.styleable.CollapsableTextView
+        private val GravityAttr = R.styleable.CollapsableTextView_android_gravity
+    }
+}
+
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
new file mode 100644
index 0000000..10e5267
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsThemeHelper.kt
@@ -0,0 +1,78 @@
+/*
+* 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.settingslib.widget
+
+import android.content.Context
+import android.os.Build
+import android.os.SystemProperties
+
+object SettingsThemeHelper {
+    private const val IS_EXPRESSIVE_DESIGN_ENABLED = "is_expressive_design_enabled"
+    private var expressiveThemeState: ExpressiveThemeState = ExpressiveThemeState.UNKNOWN
+
+    enum class ExpressiveThemeState {
+        UNKNOWN,
+        ENABLED,
+        DISABLED,
+    }
+
+    @JvmStatic
+    fun isExpressiveTheme(context: Context): Boolean {
+        tryInit(context)
+        if (expressiveThemeState == ExpressiveThemeState.UNKNOWN) {
+            throw Exception(
+                "need to call com.android.settingslib.widget.SettingsThemeHelper.init(Context) first."
+            )
+        }
+
+        return expressiveThemeState == ExpressiveThemeState.ENABLED
+    }
+
+    private fun tryInit(context: Context) {
+        if (expressiveThemeState != ExpressiveThemeState.UNKNOWN) {
+            return
+        }
+
+        expressiveThemeState =
+            if (
+                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) &&
+                    (SystemProperties.getBoolean(IS_EXPRESSIVE_DESIGN_ENABLED, false) ||
+                        getPropBoolean(context, IS_EXPRESSIVE_DESIGN_ENABLED, false))
+            ) {
+                ExpressiveThemeState.ENABLED
+            } else {
+                ExpressiveThemeState.DISABLED
+            }
+    }
+
+    private fun getPropBoolean(context: Context, property: String, def: Boolean): Boolean {
+        return try {
+            val systemProperties = context.classLoader.loadClass("android.os.SystemProperties")
+
+            val paramTypes =
+                arrayOf<Class<*>?>(String::class.java, Boolean::class.javaPrimitiveType)
+            val getBoolean = systemProperties.getMethod("getBoolean", *paramTypes)
+
+            val params = arrayOf<Any>(property, def)
+            getBoolean.invoke(systemProperties, *params) as Boolean
+        } catch (iae: IllegalArgumentException) {
+            throw iae
+        } catch (exception: Exception) {
+            def
+        }
+    }
+}
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 3011ce0..b69912a 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.7.0-rc01"
+    extra["jetpackComposeVersion"] = "1.7.0"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/gallery/res/values/strings.xml b/packages/SettingsLib/Spa/gallery/res/values/strings.xml
index 18a6db0..f942fd0 100644
--- a/packages/SettingsLib/Spa/gallery/res/values/strings.xml
+++ b/packages/SettingsLib/Spa/gallery/res/values/strings.xml
@@ -26,6 +26,8 @@
     <string name="single_line_summary_preference_summary" translatable="false">A very long summary to show case a preference which only shows a single line summary.</string>
     <!-- Footer text with two links. [DO NOT TRANSLATE] -->
     <string name="footer_with_two_links" translatable="false">Annotated string with <a href="https://www.android.com/">link 1</a> and <a href="https://source.android.com/">link 2</a>.</string>
+    <!-- TopIntroPreference preview text. [DO NOT TRANSLATE] -->
+    <string name="label_with_two_links" translatable="false"><a href="https://www.android.com/">Label</a></string>
 
     <!-- Sample title -->
     <string name="sample_title" translatable="false">Lorem ipsum</string>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 83d657e..7139f5b4 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -42,12 +42,15 @@
 import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
 import com.android.settingslib.spa.gallery.scaffold.NonScrollablePagerPageProvider
 import com.android.settingslib.spa.gallery.page.SliderPageProvider
+import com.android.settingslib.spa.gallery.preference.IntroPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
 import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.preference.TopIntroPreferencePageProvider
 import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.preference.ZeroStatePreferencePageProvider
 import com.android.settingslib.spa.gallery.scaffold.PagerMainPageProvider
 import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
 import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
@@ -82,6 +85,7 @@
                 MainSwitchPreferencePageProvider,
                 ListPreferencePageProvider,
                 TwoTargetSwitchPreferencePageProvider,
+                ZeroStatePreferencePageProvider,
                 ArgumentPageProvider,
                 SliderPageProvider,
                 SpinnerPageProvider,
@@ -109,6 +113,8 @@
                 SuwScaffoldPageProvider,
                 BannerPageProvider,
                 CopyablePageProvider,
+                IntroPreferencePageProvider,
+                TopIntroPreferencePageProvider,
             ),
             rootPages = listOf(
                 HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
deleted file mode 100644
index 1051549..0000000
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.gallery.page
-
-import android.os.Bundle
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.AccessAlarm
-import androidx.compose.material.icons.outlined.MusicNote
-import androidx.compose.material.icons.outlined.MusicOff
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.tooling.preview.Preview
-import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
-import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.preference.SliderPreference
-import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
-
-private const val TITLE = "Sample Slider"
-
-object SliderPageProvider : SettingsPageProvider {
-    override val name = "Slider"
-    private val owner = createSettingsPage()
-
-    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
-        val entryList = mutableListOf<SettingsEntry>()
-        entryList.add(
-            SettingsEntryBuilder.create("Simple Slider", owner)
-                .setUiLayoutFn {
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Simple Slider"
-                        override val initValue = 40
-                    })
-                }.build()
-        )
-        entryList.add(
-            SettingsEntryBuilder.create("Slider with icon", owner)
-                .setUiLayoutFn {
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Slider with icon"
-                        override val initValue = 30
-                        override val onValueChangeFinished = {
-                            println("onValueChangeFinished")
-                        }
-                        override val icon = Icons.Outlined.AccessAlarm
-                    })
-                }.build()
-        )
-        entryList.add(
-            SettingsEntryBuilder.create("Slider with changeable icon", owner)
-                .setUiLayoutFn {
-                    val initValue = 0
-                    var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) }
-                    var sliderPosition by remember { mutableStateOf(initValue) }
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Slider with changeable icon"
-                        override val initValue = initValue
-                        override val onValueChange = { it: Int ->
-                            sliderPosition = it
-                            icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff
-                        }
-                        override val onValueChangeFinished = {
-                            println("onValueChangeFinished: the value is $sliderPosition")
-                        }
-                        override val icon = icon
-                    })
-                }.build()
-        )
-        entryList.add(
-            SettingsEntryBuilder.create("Slider with steps", owner)
-                .setUiLayoutFn {
-                    SliderPreference(object : SliderPreferenceModel {
-                        override val title = "Slider with steps"
-                        override val initValue = 2
-                        override val valueRange = 1..5
-                        override val showSteps = true
-                    })
-                }.build()
-        )
-
-        return entryList
-    }
-
-    fun buildInjectEntry(): SettingsEntryBuilder {
-        return SettingsEntryBuilder.createInject(owner)
-            .setUiLayoutFn {
-                Preference(object : PreferenceModel {
-                    override val title = TITLE
-                    override val onClick = navigator(name)
-                })
-            }
-    }
-
-    override fun getTitle(arguments: Bundle?): String {
-        return TITLE
-    }
-}
-
-@Preview(showBackground = true)
-@Composable
-private fun SliderPagePreview() {
-    SettingsTheme {
-        SliderPageProvider.Page(null)
-    }
-}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPageProvider.kt
new file mode 100644
index 0000000..89b10ee
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPageProvider.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.page
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AccessAlarm
+import androidx.compose.material.icons.outlined.MusicNote
+import androidx.compose.material.icons.outlined.MusicOff
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.SliderPreference
+import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
+
+private const val TITLE = "Sample Slider"
+
+object SliderPageProvider : SettingsPageProvider {
+    override val name = "Slider"
+    private val owner = createSettingsPage()
+
+    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+        val entryList = mutableListOf<SettingsEntry>()
+        entryList.add(
+            SettingsEntryBuilder.create("Simple Slider", owner)
+                .setUiLayoutFn {
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Simple Slider"
+                            override val initValue = 40
+                        }
+                    )
+                }
+                .build()
+        )
+        entryList.add(
+            SettingsEntryBuilder.create("Slider with icon", owner)
+                .setUiLayoutFn {
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Slider with icon"
+                            override val initValue = 30
+                            override val onValueChangeFinished = {
+                                println("onValueChangeFinished")
+                            }
+                            override val iconStart = Icons.Outlined.AccessAlarm
+                        }
+                    )
+                }
+                .build()
+        )
+        entryList.add(
+            SettingsEntryBuilder.create("Slider with changeable icon", owner)
+                .setUiLayoutFn {
+                    val initValue = 0
+                    var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) }
+                    var sliderPosition by remember { mutableStateOf(initValue) }
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Slider with changeable icon"
+                            override val initValue = initValue
+                            override val onValueChange = { it: Int ->
+                                sliderPosition = it
+                                icon =
+                                    if (it > 0) Icons.Outlined.MusicNote
+                                    else Icons.Outlined.MusicOff
+                            }
+                            override val onValueChangeFinished = {
+                                println("onValueChangeFinished: the value is $sliderPosition")
+                            }
+                            override val iconEnd = icon
+                        }
+                    )
+                }
+                .build()
+        )
+        entryList.add(
+            SettingsEntryBuilder.create("Slider with steps", owner)
+                .setUiLayoutFn {
+                    SliderPreference(
+                        object : SliderPreferenceModel {
+                            override val title = "Slider with steps"
+                            override val initValue = 2
+                            override val valueRange = 1..5
+                            override val showSteps = true
+                        }
+                    )
+                }
+                .build()
+        )
+
+        return entryList
+    }
+
+    fun buildInjectEntry(): SettingsEntryBuilder {
+        return SettingsEntryBuilder.createInject(owner).setUiLayoutFn {
+            Preference(
+                object : PreferenceModel {
+                    override val title = TITLE
+                    override val onClick = navigator(name)
+                }
+            )
+        }
+    }
+
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SliderPagePreview() {
+    SettingsTheme { SliderPageProvider.Page(null) }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/IntroPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/IntroPreferencePageProvider.kt
new file mode 100644
index 0000000..603fcee
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/IntroPreferencePageProvider.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AirplanemodeActive
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.IntroPreference
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+private const val TITLE = "Sample IntroPreference"
+
+object IntroPreferencePageProvider : SettingsPageProvider {
+    override val name = "IntroPreference"
+    private val owner = createSettingsPage()
+
+    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+        val entryList = mutableListOf<SettingsEntry>()
+        entryList.add(
+            SettingsEntryBuilder.create("IntroPreference", owner)
+                .setUiLayoutFn { SampleIntroPreference() }
+                .build()
+        )
+
+        return entryList
+    }
+
+    fun buildInjectEntry(): SettingsEntryBuilder {
+        return SettingsEntryBuilder.createInject(owner).setUiLayoutFn {
+            Preference(
+                object : PreferenceModel {
+                    override val title = TITLE
+                    override val onClick = navigator(name)
+                }
+            )
+        }
+    }
+
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+}
+
+@Composable
+private fun SampleIntroPreference() {
+    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+        IntroPreference(
+            title = "Preferred network type",
+            descriptions = listOf("Description"),
+            imageVector = Icons.Outlined.AirplanemodeActive,
+        )
+    }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
index ce9678b..1626b02 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
@@ -39,6 +39,9 @@
             ListPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             TwoTargetSwitchPreferencePageProvider.buildInjectEntry()
                 .setLink(fromPage = owner).build(),
+            ZeroStatePreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+            IntroPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+            TopIntroPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
         )
     }
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TopIntroPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TopIntroPreferencePageProvider.kt
new file mode 100644
index 0000000..b251266
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TopIntroPreferencePageProvider.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.TopIntroPreference
+import com.android.settingslib.spa.widget.preference.TopIntroPreferenceModel
+
+private const val TITLE = "Sample TopIntroPreference"
+
+object TopIntroPreferencePageProvider : SettingsPageProvider {
+    override val name = "TopIntroPreference"
+    private val owner = createSettingsPage()
+
+    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+        val entryList = mutableListOf<SettingsEntry>()
+        entryList.add(
+            SettingsEntryBuilder.create("TopIntroPreference", owner)
+                .setUiLayoutFn { SampleTopIntroPreference() }
+                .build()
+        )
+
+        return entryList
+    }
+
+    fun buildInjectEntry(): SettingsEntryBuilder {
+        return SettingsEntryBuilder.createInject(owner).setUiLayoutFn {
+            Preference(
+                object : PreferenceModel {
+                    override val title = TITLE
+                    override val onClick = navigator(name)
+                }
+            )
+        }
+    }
+
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+}
+
+@Composable
+private fun SampleTopIntroPreference() {
+    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+        TopIntroPreference(
+            object : TopIntroPreferenceModel {
+                override val text =
+                    "Additional text needed for the page. This can sit on the right side of the screen in 2 column.\n" +
+                        "Example collapsed text area that you will not see until you expand this block."
+                override val expandText = "Expand"
+                override val collapseText = "Collapse"
+                override val labelText = R.string.label_with_two_links
+            }
+        )
+    }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ZeroStatePreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ZeroStatePreferencePageProvider.kt
new file mode 100644
index 0000000..4a9c5c8
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ZeroStatePreferencePageProvider.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.History
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.ZeroStatePreference
+
+private const val TITLE = "Sample ZeroStatePreference"
+
+object ZeroStatePreferencePageProvider : SettingsPageProvider {
+    override val name = "ZeroStatePreference"
+    private val owner = createSettingsPage()
+
+    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+        val entryList = mutableListOf<SettingsEntry>()
+        entryList.add(
+            SettingsEntryBuilder.create("ZeroStatePreference", owner)
+                .setUiLayoutFn {
+                    SampleZeroStatePreference()
+                }.build()
+        )
+
+        return entryList
+    }
+
+    fun buildInjectEntry(): SettingsEntryBuilder {
+        return SettingsEntryBuilder.createInject(owner)
+            .setUiLayoutFn {
+                Preference(object : PreferenceModel {
+                    override val title = TITLE
+                    override val onClick = navigator(name)
+                })
+            }
+    }
+
+    override fun getTitle(arguments: Bundle?): String {
+        return TITLE
+    }
+}
+
+@Composable
+private fun SampleZeroStatePreference() {
+    Box(
+        modifier = Modifier.fillMaxSize(),
+        contentAlignment = Alignment.Center
+    ) {
+        ZeroStatePreference(
+            Icons.Filled.History,
+            "No recent search history",
+            "Description"
+        )
+    }
+}
+
+
+@Preview(showBackground = true)
+@Composable
+private fun SwitchPreferencePagePreview() {
+    SettingsTheme {
+        ZeroStatePreferencePageProvider.Page(null)
+    }
+}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index d01c0b9..272dc2d 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
 #
 
 [versions]
-agp = "8.6.0"
+agp = "8.6.1"
 compose-compiler = "1.5.11"
 dexmaker-mockito = "2.28.3"
 jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
deleted file mode 100644
index 50432f3..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip
new file mode 100644
index 0000000..45f0424
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10.2-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 9a7f4b6..1c25e974 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
 
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=gradle-8.10-bin.zip
+distributionUrl=gradle-8.10.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
index 3a96a70..ca36e5b 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
@@ -60,7 +60,7 @@
                     override val onValueChangeFinished = {
                         println("onValueChangeFinished")
                     }
-                    override val icon = Icons.Outlined.AccessAlarm
+                    override val iconStart = Icons.Outlined.AccessAlarm
                 })
 
                 SliderPreference(object : SliderPreferenceModel {
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index f0c2ea6..8f8275b 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,15 +54,16 @@
 dependencies {
     api(project(":SettingsLibColor"))
     api("androidx.appcompat:appcompat:1.7.0")
-    api("androidx.compose.material3:material3:1.3.0-rc01")
+    api("androidx.compose.material3:material3:1.3.0")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
+    api("androidx.graphics:graphics-shapes-android:1.0.1")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.8.0-rc01")
+    api("androidx.navigation:navigation-compose:2.8.1")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
-    api("com.google.android.material:material:1.11.0")
+    api("com.google.android.material:material:1.12.0")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
     implementation("com.airbnb.android:lottie-compose:6.4.0")
 
@@ -83,7 +84,6 @@
 
                     // Excludes files forked from AndroidX.
                     "com/android/settingslib/spa/widget/scaffold/CustomizedAppBar*",
-                    "com/android/settingslib/spa/widget/scaffold/TopAppBarColors*",
 
                     // Excludes files forked from Accompanist.
                     "com/android/settingslib/spa/framework/compose/DrawablePainter*",
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 1f3e2425..f8c791a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -21,7 +21,9 @@
 
 object SettingsDimension {
     val paddingTiny = 2.dp
-    val paddingSmall = 4.dp
+    val paddingExtraSmall = 4.dp
+    val paddingSmall = if (isSpaExpressiveEnabled) 8.dp else 4.dp
+    val paddingExtraSmall5 = 10.dp
     val paddingLarge = 16.dp
     val paddingExtraLarge = 24.dp
 
@@ -56,6 +58,7 @@
     val itemDividerHeight = 32.dp
 
     val iconLarge = 48.dp
+    val introIconSize = 40.dp
 
     /** The size when app icon is displayed in list. */
     val appIconItemSize = 32.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
index 15def72..f948d51 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -21,6 +21,7 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import com.android.settingslib.spa.framework.util.SystemProperties
 
 /**
  * The Material 3 Theme for Settings.
@@ -41,4 +42,5 @@
     }
 }
 
-const val isSpaExpressiveEnabled = false
\ No newline at end of file
+val isSpaExpressiveEnabled
+    by lazy { SystemProperties.getBoolean("is_expressive_design_enabled", false) }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
index 460bf99..965c971 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
@@ -162,3 +162,6 @@
 /** Creates a new [TextStyle] which font weight set to medium. */
 internal fun TextStyle.toMediumWeight() =
     copy(fontWeight = FontWeight.Medium, letterSpacing = 0.01.em)
+
+internal fun TextStyle.toSemiBoldWeight() =
+    copy(fontWeight = FontWeight.SemiBold, letterSpacing = 0.01.em)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SystemProperties.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SystemProperties.kt
new file mode 100644
index 0000000..ed4936b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/SystemProperties.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.settingslib.spa.framework.util
+
+import android.annotation.SuppressLint
+import android.util.Log
+
+@SuppressLint("PrivateApi")
+object SystemProperties {
+    private const val TAG = "SystemProperties"
+
+    fun getBoolean(key: String, default: Boolean): Boolean = try {
+        val systemProperties = Class.forName("android.os.SystemProperties")
+        systemProperties
+            .getMethod("getBoolean", String::class.java, Boolean::class.java)
+            .invoke(systemProperties, key, default) as Boolean
+    } catch (e: Exception) {
+        Log.e(TAG, "getBoolean: $key", e)
+        default
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/banner/SettingsBanner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/banner/SettingsBanner.kt
index e3f4860..185fd29 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/banner/SettingsBanner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/banner/SettingsBanner.kt
@@ -19,6 +19,7 @@
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.ExperimentalLayoutApi
@@ -55,6 +56,7 @@
 import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraLarge
 import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraSmall
 import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 import com.android.settingslib.spa.widget.ui.SettingsBody
 import com.android.settingslib.spa.widget.ui.SettingsTitle
 
@@ -62,15 +64,13 @@
 fun SettingsBanner(content: @Composable ColumnScope.() -> Unit) {
     Card(
         shape = CornerExtraLarge,
-        colors = CardDefaults.cardColors(
-            containerColor = Color.Transparent,
-        ),
-        modifier = Modifier
-            .fillMaxWidth()
-            .padding(
-                horizontal = SettingsDimension.itemPaddingEnd,
-                vertical = SettingsDimension.itemPaddingAround,
-            ),
+        colors = CardDefaults.cardColors(containerColor = Color.Transparent),
+        modifier =
+            Modifier.fillMaxWidth()
+                .padding(
+                    horizontal = SettingsDimension.itemPaddingEnd,
+                    vertical = SettingsDimension.itemPaddingAround,
+                ),
         content = content,
     )
 }
@@ -81,40 +81,64 @@
     content: @Composable ColumnScope.() -> Unit,
 ) {
     Card(
-        shape = CornerExtraSmall,
-        colors = CardDefaults.cardColors(
-            containerColor = containerColor.takeOrElse { MaterialTheme.colorScheme.surface },
-        ),
-        modifier = Modifier
-            .fillMaxWidth()
-            .padding(vertical = 1.dp),
+        shape = if (isSpaExpressiveEnabled) CornerExtraLarge else CornerExtraSmall,
+        colors =
+            CardDefaults.cardColors(
+                containerColor = containerColor.takeOrElse { MaterialTheme.colorScheme.surface }
+            ),
+        modifier = Modifier.fillMaxWidth().padding(vertical = 1.dp),
         content = content,
     )
 }
 
 @Composable
 fun SettingsBanner(model: BannerModel) {
-    SettingsBanner {
-        SettingsBannerImpl(model)
-    }
+    SettingsBanner { SettingsBannerImpl(model) }
 }
 
 @Composable
 internal fun SettingsBannerImpl(model: BannerModel) {
     AnimatedVisibility(visible = model.isVisible()) {
         SettingsBannerContent(containerColor = model.containerColor) {
-            Column(
-                modifier = (model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier)
-                    .padding(
-                        horizontal = SettingsDimension.dialogItemPaddingHorizontal,
-                        vertical = SettingsDimension.itemPaddingAround,
-                    ),
-                verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
-            ) {
-                BannerHeader(model.imageVector, model.tintColor, model.onDismiss)
-                SettingsTitle(model.title)
-                SettingsBody(model.text)
-                Buttons(model.buttons, model.tintColor)
+            if (isSpaExpressiveEnabled) {
+                Column(
+                    modifier =
+                        (model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier)
+                            .padding(
+                                start = SettingsDimension.paddingLarge,
+                                end = SettingsDimension.paddingLarge,
+                                top = SettingsDimension.paddingLarge,
+                                bottom = SettingsDimension.paddingSmall,
+                            )
+                ) {
+                    Row(verticalAlignment = Alignment.CenterVertically) {
+                        BannerIcon(model.imageVector, model.tintColor)
+                        Column(
+                            modifier = Modifier.padding(start = SettingsDimension.paddingLarge),
+                            verticalArrangement =
+                                Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
+                        ) {
+                            BannerTitleHeader(model.title, model.onDismiss)
+                            SettingsBody(model.text)
+                        }
+                    }
+                    Buttons(model.buttons, model.tintColor)
+                }
+            } else {
+                Column(
+                    modifier =
+                        (model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier)
+                            .padding(
+                                horizontal = SettingsDimension.dialogItemPaddingHorizontal,
+                                vertical = SettingsDimension.itemPaddingAround,
+                            ),
+                    verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
+                ) {
+                    BannerHeader(model.imageVector, model.tintColor, model.onDismiss)
+                    SettingsTitle(model.title)
+                    SettingsBody(model.text)
+                    Buttons(model.buttons, model.tintColor)
+                }
             }
         }
     }
@@ -133,6 +157,15 @@
 }
 
 @Composable
+fun BannerTitleHeader(title: String, onDismiss: (() -> Unit)? = null) {
+    Row(Modifier.fillMaxWidth()) {
+        Box(modifier = Modifier.weight(1f)) { SettingsTitle(title) }
+        Spacer(modifier = Modifier.padding(SettingsDimension.paddingSmall))
+        DismissButton(onDismiss)
+    }
+}
+
+@Composable
 private fun BannerIcon(imageVector: ImageVector?, color: Color) {
     if (imageVector != null) {
         Icon(
@@ -147,19 +180,12 @@
 @Composable
 private fun DismissButton(onDismiss: (() -> Unit)?) {
     if (onDismiss == null) return
-    Surface(
-        shape = CircleShape,
-        color = MaterialTheme.colorScheme.secondaryContainer,
-    ) {
-        IconButton(
-            onClick = onDismiss,
-            modifier = Modifier.size(SettingsDimension.itemIconSize)
-        ) {
+    Surface(shape = CircleShape, color = MaterialTheme.colorScheme.secondaryContainer) {
+        IconButton(onClick = onDismiss, modifier = Modifier.size(SettingsDimension.itemIconSize)) {
             Icon(
                 imageVector = Icons.Outlined.Close,
-                contentDescription = stringResource(
-                    androidx.compose.material3.R.string.m3c_snackbar_dismiss
-                ),
+                contentDescription =
+                    stringResource(androidx.compose.material3.R.string.m3c_snackbar_dismiss),
                 modifier = Modifier.padding(SettingsDimension.paddingSmall),
             )
         }
@@ -172,10 +198,11 @@
     if (buttons.isNotEmpty()) {
         FlowRow(
             modifier = Modifier.fillMaxWidth(),
-            horizontalArrangement = Arrangement.spacedBy(
-                space = SettingsDimension.itemPaddingEnd,
-                alignment = Alignment.End,
-            ),
+            horizontalArrangement =
+                Arrangement.spacedBy(
+                    space = SettingsDimension.itemPaddingEnd,
+                    alignment = Alignment.End,
+                ),
         ) {
             for (button in buttons) {
                 Button(button, color)
@@ -205,9 +232,7 @@
                 title = "Lorem ipsum",
                 text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
                 imageVector = Icons.Outlined.WarningAmber,
-                buttons = listOf(
-                    BannerButton(text = "Action") {},
-                )
+                buttons = listOf(BannerButton(text = "Action") {}),
             )
         )
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 70d353d..5bb57b8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.IntrinsicSize
@@ -25,11 +26,14 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
 import androidx.compose.material.icons.outlined.Delete
 import androidx.compose.material.icons.outlined.WarningAmber
 import androidx.compose.material3.ButtonDefaults
@@ -51,7 +55,7 @@
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.theme.divider
-import androidx.compose.material.icons.automirrored.outlined.Launch
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 
 data class ActionButton(
     val text: String,
@@ -62,48 +66,64 @@
 
 @Composable
 fun ActionButtons(actionButtons: List<ActionButton>) {
-    Row(
-        Modifier
-            .padding(SettingsDimension.buttonPadding)
-            .clip(SettingsShape.CornerExtraLarge)
-            .height(IntrinsicSize.Min)
-    ) {
-        for ((index, actionButton) in actionButtons.withIndex()) {
-            if (index > 0) ButtonDivider()
-            ActionButton(actionButton)
+    if (isSpaExpressiveEnabled) {
+        Row(
+            horizontalArrangement = Arrangement.SpaceAround,
+            modifier = Modifier
+                .padding(SettingsDimension.buttonPadding)
+                .height(IntrinsicSize.Min)
+                .fillMaxWidth()
+        ) {
+            for (actionButton in actionButtons) {
+                ActionButton(actionButton)
+            }
+        }
+    } else {
+        Row(
+            Modifier
+                .padding(SettingsDimension.buttonPadding)
+                .clip(SettingsShape.CornerExtraLarge)
+                .height(IntrinsicSize.Min)
+        ) {
+            for ((index, actionButton) in actionButtons.withIndex()) {
+                if (index > 0) ButtonDivider()
+                ActionButton(actionButton)
+            }
         }
     }
 }
 
 @Composable
 private fun RowScope.ActionButton(actionButton: ActionButton) {
-    FilledTonalButton(
-        onClick = actionButton.onClick,
-        modifier = Modifier
-            .weight(1f)
-            .fillMaxHeight(),
-        enabled = actionButton.enabled,
-        // Because buttons could appear, disappear or change positions, reset the interaction source
-        // to prevent highlight the wrong button.
-        interactionSource = remember(actionButton) { MutableInteractionSource() },
-        shape = RectangleShape,
-        colors = ButtonDefaults.filledTonalButtonColors(
-            containerColor = MaterialTheme.colorScheme.surface,
-            contentColor = MaterialTheme.colorScheme.primary,
-            disabledContainerColor = MaterialTheme.colorScheme.surface,
-        ),
-        contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
-    ) {
+    if (isSpaExpressiveEnabled) {
         Column(horizontalAlignment = Alignment.CenterHorizontally) {
-            Icon(
-                imageVector = actionButton.imageVector,
-                contentDescription = null,
-                modifier = Modifier.size(SettingsDimension.itemIconSize),
-            )
+            FilledTonalButton(
+                onClick = actionButton.onClick,
+                modifier = Modifier
+                    .weight(1f)
+                    .fillMaxHeight()
+                    .clip(RoundedCornerShape(100.dp)),
+                enabled = actionButton.enabled,
+                // Because buttons could appear, disappear or change positions, reset the interaction source
+                // to prevent highlight the wrong button.
+                interactionSource = remember(actionButton) { MutableInteractionSource() },
+                shape = RectangleShape,
+                colors = ButtonDefaults.filledTonalButtonColors(
+                    containerColor = MaterialTheme.colorScheme.primaryContainer,
+                    contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+                    disabledContainerColor = MaterialTheme.colorScheme.surface,
+                ),
+                contentPadding = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
+            ) {
+                Icon(
+                    imageVector = actionButton.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(SettingsDimension.itemIconSize),
+                )
+            }
             Box(
                 modifier = Modifier
-                    .padding(top = 4.dp)
-                    .fillMaxHeight(),
+                    .padding(top = 6.dp),
                 contentAlignment = Alignment.Center,
             ) {
                 Text(
@@ -113,6 +133,44 @@
                 )
             }
         }
+    } else {
+        FilledTonalButton(
+            onClick = actionButton.onClick,
+            modifier = Modifier
+                .weight(1f)
+                .fillMaxHeight(),
+            enabled = actionButton.enabled,
+            // Because buttons could appear, disappear or change positions, reset the interaction source
+            // to prevent highlight the wrong button.
+            interactionSource = remember(actionButton) { MutableInteractionSource() },
+            shape = RectangleShape,
+            colors = ButtonDefaults.filledTonalButtonColors(
+                containerColor = MaterialTheme.colorScheme.surface,
+                contentColor = MaterialTheme.colorScheme.primary,
+                disabledContainerColor = MaterialTheme.colorScheme.surface,
+            ),
+            contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
+        ) {
+            Column(horizontalAlignment = Alignment.CenterHorizontally) {
+                Icon(
+                    imageVector = actionButton.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(SettingsDimension.itemIconSize),
+                )
+                Box(
+                    modifier = Modifier
+                        .padding(top = 4.dp)
+                        .fillMaxHeight(),
+                    contentAlignment = Alignment.Center,
+                ) {
+                    Text(
+                        text = actionButton.text,
+                        textAlign = TextAlign.Center,
+                        style = MaterialTheme.typography.labelMedium,
+                    )
+                }
+            }
+        }
     }
 }
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/IntroPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/IntroPreference.kt
new file mode 100644
index 0000000..22a5755
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/IntroPreference.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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AirplanemodeActive
+import androidx.compose.material3.Icon
+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
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+
+@Composable
+fun IntroPreference(
+    title: String,
+    descriptions: List<String>? = null,
+    imageVector: ImageVector? = null,
+) {
+    IntroPreference(title = title, descriptions = descriptions, icon = { IntroIcon(imageVector) })
+}
+
+@Composable
+fun IntroAppPreference(
+    title: String,
+    descriptions: List<String>? = null,
+    appIcon: @Composable (() -> Unit),
+) {
+    IntroPreference(title = title, descriptions = descriptions, icon = { IntroAppIcon(appIcon) })
+}
+
+@Composable
+internal fun IntroPreference(
+    title: String,
+    descriptions: List<String>?,
+    icon: @Composable (() -> Unit),
+) {
+    Column(
+        modifier =
+            Modifier.fillMaxWidth()
+                .padding(
+                    horizontal = SettingsDimension.paddingExtraLarge,
+                    vertical = SettingsDimension.paddingLarge,
+                ),
+        horizontalAlignment = Alignment.CenterHorizontally,
+    ) {
+        icon()
+        IntroTitle(title)
+        IntroDescription(descriptions)
+    }
+}
+
+@Composable
+private fun IntroIcon(imageVector: ImageVector?) {
+    if (imageVector != null) {
+        Box(
+            modifier =
+                Modifier.size(SettingsDimension.itemIconContainerSize)
+                    .clip(CircleShape)
+                    .background(MaterialTheme.colorScheme.secondaryContainer),
+            contentAlignment = Alignment.Center,
+        ) {
+            Icon(
+                imageVector = imageVector,
+                contentDescription = null,
+                modifier = Modifier.size(SettingsDimension.introIconSize),
+                tint = MaterialTheme.colorScheme.onSecondary,
+            )
+        }
+    }
+}
+
+@Composable
+private fun IntroAppIcon(appIcon: @Composable () -> Unit) {
+    Box(
+        modifier = Modifier.size(SettingsDimension.itemIconContainerSize).clip(CircleShape),
+        contentAlignment = Alignment.Center,
+    ) {
+        appIcon()
+    }
+}
+
+@Composable
+private fun IntroTitle(title: String) {
+    Box(modifier = Modifier.padding(top = SettingsDimension.paddingLarge)) {
+        Text(
+            text = title,
+            textAlign = TextAlign.Center,
+            style = MaterialTheme.typography.titleLarge,
+            color = MaterialTheme.colorScheme.onSurface,
+        )
+    }
+}
+
+@Composable
+private fun IntroDescription(descriptions: List<String>?) {
+    if (descriptions != null) {
+        for (description in descriptions) {
+            if (description.isEmpty()) continue
+            Text(
+                text = description,
+                textAlign = TextAlign.Center,
+                style = MaterialTheme.typography.titleMedium,
+                color = MaterialTheme.colorScheme.onSurfaceVariant,
+                modifier = Modifier.padding(top = SettingsDimension.paddingExtraSmall),
+            )
+        }
+    }
+}
+
+@Preview
+@Composable
+private fun IntroPreferencePreview() {
+    IntroPreference(
+        title = "Preferred network type",
+        descriptions = listOf("Description", "Version"),
+        imageVector = Icons.Outlined.AirplanemodeActive,
+    )
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
index 7bca38f..b2a3aac 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
@@ -16,6 +16,10 @@
 
 package com.android.settingslib.spa.widget.preference
 
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.AccessAlarm
 import androidx.compose.material.icons.outlined.MusicNote
@@ -27,30 +31,25 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.util.EntryHighlight
 import com.android.settingslib.spa.widget.ui.SettingsSlider
 
-/**
- * The widget model for [SliderPreference] widget.
- */
+/** The widget model for [SliderPreference] widget. */
 interface SliderPreferenceModel {
-    /**
-     * The title of this [SliderPreference].
-     */
+    /** The title of this [SliderPreference]. */
     val title: String
 
-    /**
-     * The initial position of the [SliderPreference].
-     */
+    /** The initial position of the [SliderPreference]. */
     val initValue: Int
 
-    /**
-     * The value range for this [SliderPreference].
-     */
+    /** The value range for this [SliderPreference]. */
     val valueRange: IntRange
         get() = 0..100
 
@@ -69,15 +68,23 @@
         get() = null
 
     /**
-     * The icon image for [SliderPreference]. If not specified, the slider hides the icon by default.
+     * The start icon image for [SliderPreference]. If not specified, the slider hides the icon by
+     * default.
      */
-    val icon: ImageVector?
+    val iconStart: ImageVector?
         get() = null
 
     /**
-     * Indicates whether to show step marks. If show step marks, when user finish sliding,
-     * the slider will automatically jump to the nearest step mark. Otherwise, the slider hides
-     * the step marks by default.
+     * The end icon image for [SliderPreference]. If not specified, the slider hides the icon by
+     * default.
+     */
+    val iconEnd: ImageVector?
+        get() = null
+
+    /**
+     * Indicates whether to show step marks. If show step marks, when user finish sliding, the
+     * slider will automatically jump to the nearest step mark. Otherwise, the slider hides the step
+     * marks by default.
      *
      * The step is fixed to 1.
      */
@@ -99,7 +106,8 @@
             valueRange = model.valueRange,
             onValueChange = model.onValueChange,
             onValueChangeFinished = model.onValueChangeFinished,
-            icon = model.icon,
+            iconStart = model.iconStart,
+            iconEnd = model.iconEnd,
             showSteps = model.showSteps,
         )
     }
@@ -113,27 +121,45 @@
     valueRange: IntRange = 0..100,
     onValueChange: ((value: Int) -> Unit)? = null,
     onValueChangeFinished: (() -> Unit)? = null,
-    icon: ImageVector? = null,
+    iconStart: ImageVector? = null,
+    iconEnd: ImageVector? = null,
     showSteps: Boolean = false,
 ) {
+
     BaseLayout(
         title = title,
         subTitle = {
-            SettingsSlider(
-                initValue,
-                modifier,
-                valueRange,
-                onValueChange,
-                onValueChangeFinished,
-                showSteps
-            )
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                if (iconStart != null) {
+                    SliderIcon(icon = iconStart)
+                    Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+                }
+                Box(Modifier.weight(1f)) {
+                    SettingsSlider(
+                        initValue,
+                        modifier,
+                        valueRange,
+                        onValueChange,
+                        onValueChangeFinished,
+                        showSteps,
+                    )
+                }
+                if (iconEnd != null) {
+                    Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+                    SliderIcon(icon = iconEnd)
+                }
+            }
         },
-        icon = if (icon != null) ({
-            Icon(imageVector = icon, contentDescription = null)
-        }) else null,
     )
 }
 
+@Composable
+fun SliderIcon(icon: ImageVector) {
+    Box(modifier = Modifier.padding(8.dp), contentAlignment = Alignment.Center) {
+        Icon(imageVector = icon, contentDescription = null)
+    }
+}
+
 @Preview
 @Composable
 private fun SliderPreferencePreview() {
@@ -147,7 +173,7 @@
             onValueChangeFinished = {
                 println("onValueChangeFinished: the value is $sliderPosition")
             },
-            icon = Icons.Outlined.AccessAlarm,
+            iconStart = Icons.Outlined.AccessAlarm,
         )
     }
 }
@@ -163,7 +189,22 @@
             onValueChange = { it: Int ->
                 icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff
             },
-            icon = icon,
+            iconEnd = icon,
+        )
+    }
+}
+
+@Preview
+@Composable
+private fun SliderPreferenceTwoIconPreview() {
+    SettingsTheme {
+        val iconStart by remember { mutableStateOf(Icons.Outlined.MusicNote) }
+        val iconEnd by remember { mutableStateOf(Icons.Outlined.MusicOff) }
+        SliderPreference(
+            title = "Media Volume",
+            initValue = 40,
+            iconStart = iconStart,
+            iconEnd = iconEnd,
         )
     }
 }
@@ -172,11 +213,6 @@
 @Composable
 private fun SliderPreferenceStepsPreview() {
     SettingsTheme {
-        SliderPreference(
-            title = "Display Text",
-            initValue = 2,
-            valueRange = 1..5,
-            showSteps = true,
-        )
+        SliderPreference(title = "Display Text", initValue = 2, valueRange = 1..5, showSteps = true)
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TopIntroPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TopIntroPreference.kt
new file mode 100644
index 0000000..7e61959
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TopIntroPreference.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.annotation.StringRes
+import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.KeyboardArrowDown
+import androidx.compose.material.icons.filled.KeyboardArrowUp
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.toMediumWeight
+import com.android.settingslib.spa.framework.util.annotatedStringResource
+
+/** The widget model for [TopIntroPreference] widget. */
+interface TopIntroPreferenceModel {
+    /** The content of this [TopIntroPreference]. */
+    val text: String
+
+    /** The text clicked to expand this [TopIntroPreference]. */
+    val expandText: String
+
+    /** The text clicked to collapse this [TopIntroPreference]. */
+    val collapseText: String
+
+    /** The text clicked to open other resources. Should be a resource Id. */
+    val labelText: Int?
+}
+
+@Composable
+fun TopIntroPreference(model: TopIntroPreferenceModel) {
+    var expanded by remember { mutableStateOf(false) }
+    Column(Modifier.background(MaterialTheme.colorScheme.surfaceContainer)) {
+        // TopIntroPreference content.
+        Column(
+            modifier =
+                Modifier.padding(
+                        horizontal = SettingsDimension.paddingExtraLarge,
+                        vertical = SettingsDimension.paddingSmall,
+                    )
+                    .animateContentSize()
+        ) {
+            Text(
+                text = model.text,
+                style = MaterialTheme.typography.bodyLarge,
+                maxLines = if (expanded) MAX_LINE else MIN_LINE,
+            )
+            if (expanded) TopIntroAnnotatedText(model.labelText)
+        }
+
+        // TopIntroPreference collapse bar.
+        Row(
+            verticalAlignment = Alignment.CenterVertically,
+            modifier =
+                Modifier.fillMaxWidth()
+                    .clickable(onClick = { expanded = !expanded })
+                    .padding(
+                        top = SettingsDimension.paddingSmall,
+                        bottom = SettingsDimension.paddingLarge,
+                        start = SettingsDimension.paddingExtraLarge,
+                        end = SettingsDimension.paddingExtraLarge,
+                    ),
+        ) {
+            Icon(
+                imageVector =
+                    if (expanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
+                contentDescription = null,
+                modifier =
+                    Modifier.size(SettingsDimension.itemIconSize)
+                        .clip(CircleShape)
+                        .background(MaterialTheme.colorScheme.surfaceContainerHighest),
+            )
+            Text(
+                text = if (expanded) model.collapseText else model.expandText,
+                modifier = Modifier.padding(start = SettingsDimension.paddingSmall),
+                style = MaterialTheme.typography.bodyLarge.toMediumWeight(),
+                color = MaterialTheme.colorScheme.onSurface,
+            )
+        }
+    }
+}
+
+@Composable
+private fun TopIntroAnnotatedText(@StringRes id: Int?) {
+    if (id != null) {
+        Box(
+            Modifier.padding(
+                top = SettingsDimension.paddingExtraSmall5,
+                bottom = SettingsDimension.paddingExtraSmall5,
+                end = SettingsDimension.paddingLarge,
+            )
+        ) {
+            Text(
+                text = annotatedStringResource(id),
+                style = MaterialTheme.typography.bodyLarge.toMediumWeight(),
+                color = MaterialTheme.colorScheme.primary,
+            )
+        }
+    }
+}
+
+@Preview
+@Composable
+private fun TopIntroPreferencePreview() {
+    TopIntroPreference(
+        object : TopIntroPreferenceModel {
+            override val text =
+                "Additional text needed for the page. This can sit on the right side of the screen in 2 column.\n" +
+                    "Example collapsed text area that you will not see until you expand this block."
+            override val expandText = "Expand"
+            override val collapseText = "Collapse"
+            override val labelText = androidx.appcompat.R.string.abc_prepend_shortcut_label
+        }
+    )
+}
+
+const val MIN_LINE = 2
+const val MAX_LINE = 10
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index 3f68804..92b1005 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -17,18 +17,27 @@
 package com.android.settingslib.spa.widget.preference
 
 import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ChevronRight
+import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
 import com.android.settingslib.spa.framework.theme.divider
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
+import com.android.settingslib.spa.framework.util.wrapOnClickWithLog
 
 @Composable
 internal fun TwoTargetPreference(
@@ -40,21 +49,45 @@
     widget: @Composable () -> Unit,
 ) {
     Row(
-        modifier = Modifier
-            .fillMaxWidth()
-            .padding(end = SettingsDimension.itemPaddingEnd),
+        modifier = Modifier.fillMaxWidth().padding(end = SettingsDimension.itemPaddingEnd),
         verticalAlignment = Alignment.CenterVertically,
     ) {
-        Box(modifier = Modifier.weight(1f)) {
-            Preference(
-                object : PreferenceModel {
-                    override val title = title
-                    override val summary = summary
-                    override val icon = icon
-                    override val enabled = primaryEnabled
-                    override val onClick = primaryOnClick
+        if (isSpaExpressiveEnabled) {
+            val onClickWithLog = wrapOnClickWithLog(primaryOnClick)
+            val enabled = primaryEnabled()
+            val modifier =
+                if (onClickWithLog != null) {
+                    Modifier.clickable(enabled = enabled, onClick = onClickWithLog)
+                } else Modifier
+            Box(modifier = Modifier.weight(1f)) {
+                BasePreference(
+                    title = title,
+                    summary = summary,
+                    icon = icon,
+                    enabled = primaryEnabled,
+                    modifier = modifier,
+                    paddingEnd = 0.dp,
+                ) {
+                    val alphaModifier = Modifier.alphaForEnabled(primaryEnabled())
+                    Icon(
+                        imageVector = Icons.Filled.ChevronRight,
+                        contentDescription = null,
+                        modifier = alphaModifier.size(18.dp),
+                    )
                 }
-            )
+            }
+        } else {
+            Box(modifier = Modifier.weight(1f)) {
+                Preference(
+                    object : PreferenceModel {
+                        override val title = title
+                        override val summary = summary
+                        override val icon = icon
+                        override val enabled = primaryEnabled
+                        override val onClick = primaryOnClick
+                    }
+                )
+            }
         }
         PreferenceDivider()
         widget()
@@ -64,8 +97,7 @@
 @Composable
 private fun PreferenceDivider() {
     Box(
-        Modifier
-            .padding(horizontal = SettingsDimension.itemPaddingEnd)
+        Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd)
             .size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
             .background(color = MaterialTheme.colorScheme.divider)
     )
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ZeroStatePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ZeroStatePreference.kt
new file mode 100644
index 0000000..3f2e772
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ZeroStatePreference.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.History
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Matrix
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.asComposePath
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.graphics.shapes.CornerRounding
+import androidx.graphics.shapes.RoundedPolygon
+import androidx.graphics.shapes.star
+import androidx.graphics.shapes.toPath
+
+@Composable
+fun ZeroStatePreference(icon: ImageVector, text: String? = null, description: String? = null) {
+    val zeroStateShape = remember {
+        RoundedPolygon.star(
+            numVerticesPerRadius = 6,
+            innerRadius = 0.75f,
+            rounding = CornerRounding(0.3f)
+        )
+    }
+    val clip = remember(zeroStateShape) {
+        RoundedPolygonShape(polygon = zeroStateShape)
+    }
+    Column(horizontalAlignment = Alignment.CenterHorizontally) {
+        Box(
+            modifier = Modifier
+                .clip(clip)
+                .background(MaterialTheme.colorScheme.primary)
+                .size(160.dp)
+        ) {
+            Icon(
+                imageVector = icon,
+                modifier = Modifier
+                    .align(Alignment.Center)
+                    .size(72.dp),
+                tint = MaterialTheme.colorScheme.onPrimary,
+                contentDescription = null,
+            )
+        }
+        if (text != null) {
+            Text(
+                text = text,
+                textAlign = TextAlign.Center,
+                style = MaterialTheme.typography.titleMedium,
+                color = MaterialTheme.colorScheme.onSurfaceVariant,
+                modifier = Modifier.padding(top = 24.dp),
+            )
+        }
+        if (description != null) {
+            Box {
+                Text(
+                    text = description,
+                    textAlign = TextAlign.Center,
+                    style = MaterialTheme.typography.bodyMedium,
+                    color = MaterialTheme.colorScheme.onSurfaceVariant,
+                )
+            }
+        }
+    }
+}
+
+@Preview
+@Composable
+private fun ZeroStatePreferencePreview() {
+    ZeroStatePreference(
+        Icons.Filled.History,
+        "No recent search history",
+        "Description"
+    )
+}
+
+class RoundedPolygonShape(
+    private val polygon: RoundedPolygon,
+    private var matrix: Matrix = Matrix()
+) : Shape {
+    private var path = Path()
+    override fun createOutline(
+        size: Size,
+        layoutDirection: LayoutDirection,
+        density: Density
+    ): Outline {
+        path.rewind()
+        path = polygon.toPath().asComposePath()
+
+        matrix.reset()
+        matrix.scale(size.width / 2f, size.height / 2f)
+        matrix.translate(1f, 1f)
+        matrix.rotateZ(30.0f)
+
+        path.transform(matrix)
+        return Outline.Generic(path)
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 9bbc16d..f99d206 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -41,9 +41,7 @@
 internal fun NavigateBack() {
     val navController = LocalNavController.current
     val contentDescription = stringResource(R.string.abc_action_bar_up_description)
-    BackAction(contentDescription) {
-        navController.navigateBack()
-    }
+    BackAction(contentDescription) { navController.navigateBack() }
 }
 
 /** Action that collapses the search bar. */
@@ -55,15 +53,35 @@
 
 @Composable
 private fun BackAction(contentDescription: String, onClick: () -> Unit) {
-    IconButton(onClick) {
+    IconButton(
+        onClick = onClick,
+        modifier =
+        if (isSpaExpressiveEnabled)
+            Modifier
+                .padding(
+                    start = SettingsDimension.paddingLarge,
+                    end = SettingsDimension.paddingSmall,
+                    top = SettingsDimension.paddingExtraSmall,
+                    bottom = SettingsDimension.paddingExtraSmall,
+                )
+                .size(SettingsDimension.actionIconWidth, SettingsDimension.actionIconHeight)
+                .clip(SettingsShape.CornerExtraLarge)
+        else Modifier,
+    ) {
         Icon(
             imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
             contentDescription = contentDescription,
-            modifier = if (isSpaExpressiveEnabled) Modifier
-                .size(SettingsDimension.actionIconWidth, SettingsDimension.actionIconHeight)
-                .clip(SettingsShape.CornerExtraLarge)
-                .background(MaterialTheme.colorScheme.onSurfaceVariant)
-                .padding(SettingsDimension.actionIconPadding) else Modifier
+            modifier =
+            if (isSpaExpressiveEnabled)
+                Modifier
+                    .size(
+                        SettingsDimension.actionIconWidth,
+                        SettingsDimension.actionIconHeight,
+                    )
+                    .clip(SettingsShape.CornerExtraLarge)
+                    .background(MaterialTheme.colorScheme.surfaceContainerHighest)
+                    .padding(SettingsDimension.actionIconPadding)
+            else Modifier,
         )
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 2ae3b56..2c55779 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -78,7 +78,9 @@
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 import com.android.settingslib.spa.framework.theme.settingsBackground
+import com.android.settingslib.spa.framework.theme.toSemiBoldWeight
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.roundToInt
@@ -116,8 +118,12 @@
 ) {
     TwoRowsTopAppBar(
         title = { Title(title = title, maxLines = 3) },
-        titleTextStyle = MaterialTheme.typography.displaySmall,
-        smallTitleTextStyle = MaterialTheme.typography.titleMedium,
+        titleTextStyle =
+            if (isSpaExpressiveEnabled) MaterialTheme.typography.displaySmall.toSemiBoldWeight()
+            else MaterialTheme.typography.displaySmall,
+        smallTitleTextStyle =
+            if (isSpaExpressiveEnabled) MaterialTheme.typography.titleLarge.toSemiBoldWeight()
+            else MaterialTheme.typography.titleLarge,
         titleBottomPadding = LargeTitleBottomPadding,
         smallTitle = { Title(title = title, maxLines = 1) },
         modifier = modifier,
@@ -136,7 +142,9 @@
         text = title,
         modifier =
             Modifier.padding(
-                    start = SettingsDimension.itemPaddingAround,
+                    start =
+                        if (isSpaExpressiveEnabled) SettingsDimension.paddingExtraSmall
+                        else SettingsDimension.itemPaddingAround,
                     end = SettingsDimension.itemPaddingEnd,
                 )
                 .semantics { heading() },
@@ -194,7 +202,7 @@
         return lerp(
             containerColor,
             scrolledContainerColor,
-            FastOutLinearInEasing.transform(colorTransitionFraction)
+            FastOutLinearInEasing.transform(colorTransitionFraction),
         )
     }
 
@@ -241,7 +249,7 @@
             Row(
                 horizontalArrangement = Arrangement.End,
                 verticalAlignment = Alignment.CenterVertically,
-                content = actions
+                content = actions,
             )
         }
 
@@ -296,7 +304,7 @@
     windowInsets: WindowInsets,
     colors: TopAppBarColors,
     pinnedHeight: Dp,
-    scrollBehavior: TopAppBarScrollBehavior?
+    scrollBehavior: TopAppBarScrollBehavior?,
 ) {
     if (MaxHeightWithoutTitle <= pinnedHeight) {
         throw IllegalArgumentException(
@@ -333,7 +341,7 @@
             Row(
                 horizontalArrangement = Arrangement.End,
                 verticalAlignment = Alignment.CenterVertically,
-                content = actions
+                content = actions,
             )
         }
     val topTitleAlpha = { TopTitleAlphaEasing.transform(colorTransitionFraction()) }
@@ -356,9 +364,9 @@
                         scrollBehavior.state,
                         velocity,
                         scrollBehavior.flingAnimationSpec,
-                        scrollBehavior.snapAnimationSpec
+                        scrollBehavior.snapAnimationSpec,
                     )
-                }
+                },
             )
         } else {
             Modifier
@@ -412,7 +420,8 @@
                                 val measuredMaxHeightPx =
                                     density.run {
                                         MaxHeightWithoutTitle.toPx() +
-                                            coordinates.size.height.toFloat()
+                                            coordinates.size.height.toFloat() +
+                                            titleBaselineHeight.toPx()
                                     }
                                 // Allow larger max height for multi-line title, but do not reduce
                                 // max height to prevent flaky.
@@ -430,7 +439,7 @@
                 titleBottomPadding = titleBottomPaddingPx,
                 hideTitleSemantics = hideBottomRowSemantics,
                 navigationIcon = {},
-                actions = {}
+                actions = {},
             )
         }
     }
@@ -485,7 +494,7 @@
             Box(Modifier.layoutId("navigationIcon").padding(start = TopAppBarHorizontalPadding)) {
                 CompositionLocalProvider(
                     LocalContentColor provides navigationIconContentColor,
-                    content = navigationIcon
+                    content = navigationIcon,
                 )
             }
             Box(
@@ -504,18 +513,18 @@
                                     fontScale = if (titleScaleDisabled) 1f else fontScale,
                                 )
                             },
-                        content = title
+                        content = title,
                     )
                 }
             }
             Box(Modifier.layoutId("actionIcons").padding(end = TopAppBarHorizontalPadding)) {
                 CompositionLocalProvider(
                     LocalContentColor provides actionIconContentColor,
-                    content = actions
+                    content = actions,
                 )
             }
         },
-        modifier = modifier
+        modifier = modifier,
     ) { measurables, constraints ->
         val navigationIconPlaceable =
             measurables
@@ -552,7 +561,7 @@
             // Navigation icon
             navigationIconPlaceable.placeRelative(
                 x = 0,
-                y = (layoutHeight - navigationIconPlaceable.height) / 2
+                y = (layoutHeight - navigationIconPlaceable.height) / 2,
             )
 
             // Title
@@ -570,17 +579,17 @@
                                     titlePlaceable.height -
                                     max(
                                         0,
-                                        titleBottomPadding - titlePlaceable.height + titleBaseline
+                                        titleBottomPadding - titlePlaceable.height + titleBaseline,
                                     )
                         // Arrangement.Top
                         else -> 0
-                    }
+                    },
             )
 
             // Action icons
             actionIconsPlaceable.placeRelative(
                 x = constraints.maxWidth - actionIconsPlaceable.width,
-                y = (layoutHeight - actionIconsPlaceable.height) / 2
+                y = (layoutHeight - actionIconsPlaceable.height) / 2,
             )
         }
     }
@@ -595,7 +604,7 @@
     state: TopAppBarState,
     velocity: Float,
     flingAnimationSpec: DecayAnimationSpec<Float>?,
-    snapAnimationSpec: AnimationSpec<Float>?
+    snapAnimationSpec: AnimationSpec<Float>?,
 ): Velocity {
     // Check if the app bar is completely collapsed/expanded. If so, no need to settle the app bar,
     // and just return Zero Velocity.
@@ -609,20 +618,18 @@
     // continue the motion to expand or collapse the app bar.
     if (flingAnimationSpec != null && abs(velocity) > 1f) {
         var lastValue = 0f
-        AnimationState(
-                initialValue = 0f,
-                initialVelocity = velocity,
-            )
-            .animateDecay(flingAnimationSpec) {
-                val delta = value - lastValue
-                val initialHeightOffset = state.heightOffset
-                state.heightOffset = initialHeightOffset + delta
-                val consumed = abs(initialHeightOffset - state.heightOffset)
-                lastValue = value
-                remainingVelocity = this.velocity
-                // avoid rounding errors and stop if anything is unconsumed
-                if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
-            }
+        AnimationState(initialValue = 0f, initialVelocity = velocity).animateDecay(
+            flingAnimationSpec
+        ) {
+            val delta = value - lastValue
+            val initialHeightOffset = state.heightOffset
+            state.heightOffset = initialHeightOffset + delta
+            val consumed = abs(initialHeightOffset - state.heightOffset)
+            lastValue = value
+            remainingVelocity = this.velocity
+            // avoid rounding errors and stop if anything is unconsumed
+            if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
+        }
     }
     // Snap if animation specs were provided.
     if (snapAnimationSpec != null) {
@@ -633,7 +640,7 @@
                 } else {
                     state.heightOffsetLimit
                 },
-                animationSpec = snapAnimationSpec
+                animationSpec = snapAnimationSpec,
             ) {
                 state.heightOffset = value
             }
@@ -647,9 +654,10 @@
 // Medium or Large app bar.
 private val TopTitleAlphaEasing = CubicBezierEasing(.8f, 0f, .8f, .15f)
 
-internal val MaxHeightWithoutTitle = 124.dp
+internal val MaxHeightWithoutTitle = if (isSpaExpressiveEnabled) 84.dp else 124.dp
 internal val DefaultTitleHeight = 52.dp
 internal val ContainerHeight = 56.dp
+private val titleBaselineHeight = if (isSpaExpressiveEnabled) 8.dp else 0.dp
 private val LargeTitleBottomPadding = 28.dp
 private val TopAppBarHorizontalPadding = 4.dp
 
diff --git a/packages/SettingsLib/Spa/tests/res/values/strings.xml b/packages/SettingsLib/Spa/tests/res/values/strings.xml
index fb8f878..346f69b 100644
--- a/packages/SettingsLib/Spa/tests/res/values/strings.xml
+++ b/packages/SettingsLib/Spa/tests/res/values/strings.xml
@@ -28,5 +28,7 @@
 
     <string name="test_annotated_string_resource">Annotated string with <b>bold</b> and <a href="https://www.android.com/">link</a>.</string>
 
+    <string name="test_top_intro_preference_label"><a href="https://www.android.com/">Label</a></string>
+
     <string name="test_link"><a href="https://www.android.com/">link</a></string>
 </resources>
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/SystemPropertiesTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/SystemPropertiesTest.kt
new file mode 100644
index 0000000..0827fa9
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/SystemPropertiesTest.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.settingslib.spa.framework.util
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SystemPropertiesTest {
+
+    @Test
+    fun getBoolean_noCrash() {
+        SystemProperties.getBoolean("is_expressive_design_enabled", false)
+    }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/IntroPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/IntroPreferenceTest.kt
new file mode 100644
index 0000000..5d80145
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/IntroPreferenceTest.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class IntroPreferenceTest {
+    @get:Rule val composeTestRule = createComposeRule()
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent { IntroPreference(title = TITLE) }
+
+        composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+    }
+
+    @Test
+    fun description_displayed() {
+        composeTestRule.setContent { IntroPreference(title = TITLE, descriptions = DESCRIPTION) }
+
+        composeTestRule.onNodeWithText(DESCRIPTION.component1()).assertIsDisplayed()
+        composeTestRule.onNodeWithText(DESCRIPTION.component2()).assertIsNotDisplayed()
+    }
+
+    private companion object {
+        const val TITLE = "Title"
+        val DESCRIPTION = listOf("Description", "")
+    }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TopIntroPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TopIntroPreferenceTest.kt
new file mode 100644
index 0000000..62a71d4
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TopIntroPreferenceTest.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.settingslib.spa.widget.preference
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.test.R
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TopIntroPreferenceTest {
+    @get:Rule val composeTestRule = createComposeRule()
+
+    @Test
+    fun content_collapsed_displayed() {
+        composeTestRule.setContent {
+            TopIntroPreference(
+                object : TopIntroPreferenceModel {
+                    override val text = TEXT
+                    override val expandText = EXPAND_TEXT
+                    override val collapseText = COLLAPSE_TEXT
+                    override val labelText = R.string.test_top_intro_preference_label
+                }
+            )
+        }
+
+        composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+        composeTestRule.onNodeWithText(EXPAND_TEXT).assertIsDisplayed()
+    }
+
+    @Test
+    fun content_expended_displayed() {
+        composeTestRule.setContent {
+            TopIntroPreference(
+                object : TopIntroPreferenceModel {
+                    override val text = TEXT
+                    override val expandText = EXPAND_TEXT
+                    override val collapseText = COLLAPSE_TEXT
+                    override val labelText = R.string.test_top_intro_preference_label
+                }
+            )
+        }
+
+        composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+        composeTestRule.onNodeWithText(EXPAND_TEXT).assertIsDisplayed().performClick()
+        composeTestRule.onNodeWithText(COLLAPSE_TEXT).assertIsDisplayed()
+        composeTestRule.onNodeWithText(LABEL_TEXT).assertIsDisplayed()
+    }
+
+    private companion object {
+        const val TEXT = "Text"
+        const val EXPAND_TEXT = "Expand"
+        const val COLLAPSE_TEXT = "Collapse"
+        const val LABEL_TEXT = "Label"
+    }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ZeroStatePreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ZeroStatePreferenceTest.kt
new file mode 100644
index 0000000..99ac27c
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ZeroStatePreferenceTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.History
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ZeroStatePreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent {
+            ZeroStatePreference(Icons.Filled.History, TITLE)
+        }
+
+        composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+    }
+
+    @Test
+    fun description_displayed() {
+        composeTestRule.setContent {
+            ZeroStatePreference(Icons.Filled.History, TITLE, DESCRIPTION)
+        }
+
+        composeTestRule.onNodeWithText(DESCRIPTION).assertIsDisplayed()
+    }
+
+    private companion object {
+        const val TITLE = "Title"
+        const val DESCRIPTION = "Description"
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index e70201b..76e36dc 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -14,7 +14,10 @@
         "SettingsLintDefaults",
     ],
 
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     resource_dirs: ["res"],
 
     static_libs: [
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v35/settingslib_expressive_top_intro.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v35/settingslib_expressive_top_intro.xml
new file mode 100644
index 0000000..fb13ef7
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v35/settingslib_expressive_top_intro.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+
+    <com.android.settingslib.widget.CollapsableTextView
+        android:id="@+id/collapsable_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
deleted file mode 100644
index 1bbd76d..0000000
--- a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2020 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.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.widget.preference.topintro.R;
-
-/**
- * The TopIntroPreference shows a text which describe a feature. Gernerally, we expect this
- * preference always shows on the top of screen.
- */
-public class TopIntroPreference extends Preference {
-
-    public TopIntroPreference(Context context) {
-        super(context);
-        setLayoutResource(R.layout.top_intro_preference);
-        setSelectable(false);
-    }
-
-    public TopIntroPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setLayoutResource(R.layout.top_intro_preference);
-        setSelectable(false);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        super.onBindViewHolder(holder);
-        holder.setDividerAllowedAbove(false);
-        holder.setDividerAllowedBelow(false);
-    }
-}
diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
new file mode 100644
index 0000000..afced0c
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
@@ -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.settingslib.widget
+
+import android.content.Context
+import android.os.Build
+import android.util.AttributeSet
+import androidx.annotation.RequiresApi
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.preference.topintro.R
+
+open class TopIntroPreference @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0
+) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+
+    private var isCollapsable: Boolean = false
+    private var minLines: Int = 2
+
+    init {
+        if (SettingsThemeHelper.isExpressiveTheme(context)) {
+            layoutResource = R.layout.settingslib_expressive_top_intro
+            initAttributes(context, attrs, defStyleAttr)
+        } else {
+            layoutResource = R.layout.top_intro_preference
+        }
+        isSelectable = false
+    }
+
+    private fun initAttributes(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
+        context.obtainStyledAttributes(
+            attrs,
+            COLLAPSABLE_TEXT_VIEW_ATTRS, defStyleAttr, 0
+        ).apply {
+            isCollapsable = getBoolean(IS_COLLAPSABLE, false)
+            minLines = getInt(
+                MIN_LINES,
+                if (isCollapsable) DEFAULT_MIN_LINES else DEFAULT_MAX_LINES
+            ).coerceIn(1, DEFAULT_MAX_LINES)
+            recycle()
+        }
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder) {
+        super.onBindViewHolder(holder)
+        holder.isDividerAllowedAbove = false
+        holder.isDividerAllowedBelow = false
+
+        if (!SettingsThemeHelper.isExpressiveTheme(context)) {
+            return
+        }
+
+        (holder.findViewById(R.id.collapsable_text_view) as? CollapsableTextView)?.apply {
+            setCollapsable(isCollapsable)
+            setMinLines(minLines)
+            setText(title.toString())
+        }
+    }
+
+    /**
+     * Sets whether the text view is collapsable.
+     * @param collapsable True if the text view should be collapsable, false otherwise.
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    fun setCollapsable(collapsable: Boolean) {
+        isCollapsable = collapsable
+        notifyChanged()
+    }
+
+    /**
+     * Sets the minimum number of lines to display when collapsed.
+     * @param lines The minimum number of lines.
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    fun setMinLines(lines: Int) {
+        minLines = lines.coerceIn(1, DEFAULT_MAX_LINES)
+        notifyChanged()
+    }
+
+    companion object {
+        private const val DEFAULT_MAX_LINES = 10
+        private const val DEFAULT_MIN_LINES = 2
+
+        private val COLLAPSABLE_TEXT_VIEW_ATTRS =
+            com.android.settingslib.widget.theme.R.styleable.CollapsableTextView
+        private val MIN_LINES =
+            com.android.settingslib.widget.theme.R.styleable.CollapsableTextView_android_minLines
+        private val IS_COLLAPSABLE =
+            com.android.settingslib.widget.theme.R.styleable.CollapsableTextView_isCollapsable
+    }
+}
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v35/settingslib_expressive_preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v35/settingslib_expressive_preference_two_target.xml
new file mode 100644
index 0000000..4347ef2
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v35/settingslib_expressive_preference_two_target.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:clipToPadding="false">
+
+    <include layout="@layout/settingslib_expressive_preference_icon_frame"/>
+
+    <include layout="@layout/settingslib_expressive_preference_text_frame" />
+
+    <include layout="@layout/settingslib_expressive_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
index b125f71..58ff0ce 100644
--- a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
+++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
@@ -72,7 +72,10 @@
     }
 
     private void init(Context context) {
-        setLayoutResource(R.layout.preference_two_target);
+        int resID = SettingsThemeHelper.isExpressiveTheme(context)
+                ? R.layout.settingslib_expressive_preference_two_target
+                : R.layout.preference_two_target;
+        setLayoutResource(resID);
         mSmallIconSize = context.getResources().getDimensionPixelSize(
                 R.dimen.two_target_pref_small_icon_size);
         mMediumIconSize = context.getResources().getDimensionPixelSize(
diff --git a/packages/SettingsLib/ZeroStatePreference/Android.bp b/packages/SettingsLib/ZeroStatePreference/Android.bp
new file mode 100644
index 0000000..4fc00bd
--- /dev/null
+++ b/packages/SettingsLib/ZeroStatePreference/Android.bp
@@ -0,0 +1,33 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SettingsLibZeroStatePreference",
+    use_resource_processor: true,
+    defaults: [
+        "SettingsLintDefaults",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.preference_preference",
+        "SettingsLibSettingsTheme",
+    ],
+    sdk_version: "system_current",
+    min_sdk_version: "28",
+    apex_available: [
+        "//apex_available:platform",
+    ],
+}
diff --git a/packages/SettingsLib/ZeroStatePreference/AndroidManifest.xml b/packages/SettingsLib/ZeroStatePreference/AndroidManifest.xml
new file mode 100644
index 0000000..51b0ab8
--- /dev/null
+++ b/packages/SettingsLib/ZeroStatePreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget.preference.zerostate">
+
+    <uses-sdk android:minSdkVersion="28" />
+
+</manifest>
diff --git a/packages/SettingsLib/ZeroStatePreference/res/drawable/settingslib_expressive_zerostate_background.xml b/packages/SettingsLib/ZeroStatePreference/res/drawable/settingslib_expressive_zerostate_background.xml
new file mode 100644
index 0000000..f42b441
--- /dev/null
+++ b/packages/SettingsLib/ZeroStatePreference/res/drawable/settingslib_expressive_zerostate_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="160dp"
+    android:height="160dp"
+    android:viewportWidth="161"
+    android:viewportHeight="161">
+    <path
+        android:pathData="M67.2,4.43C74.79,-1.41 85.35,-1.41 92.94,4.43L112.01,19.1C113.48,20.23 115.09,21.16 116.8,21.87L139.02,31.08C147.86,34.74 153.14,43.9 151.89,53.4L148.74,77.28C148.5,79.12 148.5,80.98 148.74,82.82L151.89,106.71C153.14,116.2 147.86,125.36 139.02,129.03L116.8,138.23C115.09,138.95 113.48,139.87 112.01,141L92.94,155.67C85.35,161.51 74.79,161.51 67.2,155.67L48.13,141C46.66,139.87 45.05,138.95 43.34,138.23L21.12,129.03C12.28,125.36 7,116.2 8.25,106.71L11.4,82.82C11.64,80.98 11.64,79.12 11.4,77.28L8.25,53.4C7,43.9 12.28,34.74 21.12,31.08L43.34,21.87C45.05,21.16 46.66,20.23 48.13,19.1L67.2,4.43Z"
+        android:fillColor="@color/settingslib_materialColorSurfaceContainerHigh"/>
+</vector>
diff --git a/packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml b/packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml
new file mode 100644
index 0000000..c0b195c
--- /dev/null
+++ b/packages/SettingsLib/ZeroStatePreference/res/layout/settingslib_expressive_preference_zerostate.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        tools:ignore="ContentDescription">
+
+        <ImageView
+            android:layout_width="@dimen/settingslib_expressive_zero_state_background_size"
+            android:layout_height="@dimen/settingslib_expressive_zero_state_background_size"
+            android:src="@drawable/settingslib_expressive_zerostate_background"/>
+
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="@dimen/settingslib_expressive_space_large3"
+            android:layout_height="@dimen/settingslib_expressive_space_large3"
+            android:layout_gravity="center"/>
+
+    </FrameLayout>
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="@dimen/settingslib_expressive_zero_state_title_width"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_marginTop="@dimen/settingslib_expressive_space_small4"/>
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="@dimen/settingslib_expressive_zero_state_title_width"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_marginTop="@dimen/settingslib_expressive_space_small4"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorSecondary"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/ZeroStatePreference/res/values/dimens.xml b/packages/SettingsLib/ZeroStatePreference/res/values/dimens.xml
new file mode 100644
index 0000000..e981ecc
--- /dev/null
+++ b/packages/SettingsLib/ZeroStatePreference/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <dimen name="settingslib_expressive_zero_state_background_size">160dp</dimen>
+    <dimen name="settingslib_expressive_zero_state_title_width">316dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
new file mode 100644
index 0000000..9b1ccef
--- /dev/null
+++ b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.settingslib.widget
+
+import android.content.Context
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.widget.ImageView
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+
+import com.android.settingslib.widget.preference.zerostate.R
+
+class ZeroStatePreference @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0
+) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+
+    private val iconTint: Int = context.getColor(
+        com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSecondaryContainer
+    )
+    private var tintedIcon: Drawable? = null
+
+    init {
+        isSelectable = false
+        layoutResource = R.layout.settingslib_expressive_preference_zerostate
+        icon?.let { originalIcon ->
+            tintedIcon = originalIcon.mutate().apply {
+                colorFilter = PorterDuffColorFilter(iconTint, PorterDuff.Mode.SRC_IN)
+            }
+        }
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder) {
+        super.onBindViewHolder(holder)
+
+        holder.itemView.isFocusable = false
+        holder.itemView.isClickable = false
+
+        (holder.findViewById(android.R.id.icon) as? ImageView)?.apply {
+            setImageDrawable(tintedIcon ?: icon)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_media_microphone.xml b/packages/SettingsLib/res/drawable/ic_media_microphone.xml
new file mode 100644
index 0000000..209dea5
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_media_microphone.xml
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:tint="?attr/colorControlNormal"
+        android:viewportHeight="960"
+        android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,560Q430,560 395,525Q360,490 360,440L360,200Q360,150 395,115Q430,80 480,80Q530,80 565,115Q600,150 600,200L600,440Q600,490 565,525Q530,560 480,560ZM480,320Q480,320 480,320Q480,320 480,320L480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320L480,320Q480,320 480,320Q480,320 480,320ZM440,840L440,717Q336,703 268,624Q200,545 200,440L280,440Q280,523 338.5,581.5Q397,640 480,640Q563,640 621.5,581.5Q680,523 680,440L760,440Q760,545 692,624Q624,703 520,717L520,840L440,840ZM480,480Q497,480 508.5,468.5Q520,457 520,440L520,200Q520,183 508.5,171.5Q497,160 480,160Q463,160 451.5,171.5Q440,183 440,200L440,440Q440,457 451.5,468.5Q463,480 480,480Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index f072de8..de728e2 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Hierdie foon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Hierdie tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofoon (intern)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokluidspreker"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Eksterne toestel"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Gekoppelde toestel"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Jou toestel moet herselflaai om hierdie verandering toe te pas. Herselflaai nou of kanselleer."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Bedrade oorfone"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoonsok"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-mikrofoon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Af"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Diensverskaffernetwerk verander tans"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 86f21d3..66644cb 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ይህ ስልክ"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ይህ ጡባዊ"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ይህ ኮምፒውተር (ውስጣዊ)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"ማይክሮፎን (ውስጣዊ)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"የመትከያ ድምፅ ማውጫ"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"የውጭ መሣሪያ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"የተገናኘ መሣሪያ"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ባለገመድ ጆሮ ማዳመጫ"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ባለገመድ የራስ ላይ ማዳመጫ"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"የጆሮ ማዳመጫ"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB ድምፅ ማውጫ"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"የማይክሮፎን መሰኪያ"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB ማይክሮፎን"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"አብራ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"አጥፋ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"የአገልግሎት አቅራቢ አውታረ መረብን በመቀየር ላይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 39219f4..e241c8a 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"هذا الهاتف"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"هذا الجهاز اللوحي"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"هذا الكمبيوتر (داخلي)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"ميكروفون (داخلي)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"مكبّر صوت بقاعدة إرساء"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"جهاز خارجي"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"جهاز متّصل"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"سمّاعة سلكية"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"سماعات رأس سلكية"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"سماعات رأس"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"‏مكبّر صوت USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مقبس الميكروفون"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"‏ميكروفون بمنفذ USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"مفعّلة"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"إيقاف"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"جارٍ تغيير شبكة مشغِّل شبكة الجوّال."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ae27622..4e734b9 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফ’নটো"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই টেবলেটটো"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"এই কম্পিউটাৰ (অভ্যন্তৰীণ)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"মাইক্ৰ’ফ’ন (অভ্যন্তৰীণ)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ড’ক স্পীকাৰ"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"বাহ্যিক ডিভাইচ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"সংযোগ হৈ থকা ডিভাইচ"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"তাঁৰযুক্ত হেডফ\'ন"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"তাঁৰযুক্ত হেডফ’ন"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"হেডফ’ন"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB স্পীকাৰ"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকৰ জেক"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"ইউএছবি মাইক"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"অন"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"অফ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"বাহক নেটৱৰ্কৰ পৰিৱৰ্তন"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 843c1be..4715109 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"İndicə"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu planşet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (daxili)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok dinamiki"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Xarici cihaz"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Qoşulmuş cihaz"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Naqilli qulaqlıq"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon yuvası"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktiv"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Deaktiv"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator şəbəkəsinin dəyişilməsi"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index f906634..fdd969d 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ovaj računar (interno)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (interni)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik bazne stanice"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Spoljni uređaj"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate da restartujete uređaj da bi se ova promena primenila. Restartujte ga odmah ili otkažite."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žičane slušalice"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB zvučnik"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utikač za mikrofon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključeno"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promena mreže mobilnog operatera"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 613b744..2adcc43 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Гэты тэлефон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Гэты планшэт"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Мікрафон (унутраны)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Дынамік док-станцыі"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Знешняя прылада"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Падключаная прылада"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Правадныя навушнікі"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Раздым для мікрафона"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Мікрафон USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Уключана"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Выключана"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Змяненне аператара сеткі"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 767be33..df5051f 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Този телефон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Този таблет"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Микрофон (вътрешен)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Високоговорител докинг станция"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Външно устройство"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Свързано устройство"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Слушалки с кабел"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Жак за микрофон"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Микрофон с USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Включване"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Изключване"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Промяна на мрежата на оператора"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 8419544..e7b9723 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -376,7 +376,7 @@
     <string name="strict_mode_summary" msgid="1838248687233554654">"মুখ্য থ্রেডে অ্যাপগুলির দীর্ঘ কার্যকলাপের ক্ষেত্রে স্ক্রিন ফ্ল্যাশ করে"</string>
     <string name="pointer_location" msgid="7516929526199520173">"পয়েন্টারের লোকেশন"</string>
     <string name="pointer_location_summary" msgid="957120116989798464">"স্ক্রিন ওভারলে বর্তমান স্পর্শ ডেটা দেখাচ্ছে"</string>
-    <string name="show_touches" msgid="8437666942161289025">"আলতো চাপ দেখান"</string>
+    <string name="show_touches" msgid="8437666942161289025">"ট্যাপ দেখান"</string>
     <string name="show_touches_summary" msgid="3692861665994502193">"আলতো চাপ দিলে ভিজ্যুয়াল প্রতিক্রিয়া দেখান"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"প্রেস করা কী দেখুন"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ফিজিক্যাল কী প্রেস করা হলে ভিজুয়াল ফিডব্যাক দেখুন"</string>
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফোন"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই ট্যাবলেট"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"মাইক্রোফোন (ইন্টার্নাল)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ডক স্পিকার"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"এক্সটার্নাল ডিভাইস"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"কানেক্ট থাকা ডিভাইস"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"তার যুক্ত হেডফোন"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকের জ্যাক"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB মাইক"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"চালু আছে"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"বন্ধ আছে"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"পরিষেবা প্রদানকারীর নেটওয়ার্ক পরিবর্তন করা হচ্ছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 12e8c73..e8d4a7c 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ovo računalo (interno)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (interni)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate ponovo pokrenuti uređaj da se ova promjena primijeni. Ponovo pokrenite odmah ili otkažite."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žičane slušalice"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB zvučnik"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Priključak za mikrofon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključi"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključi"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promjena mreže mobilnog operatera"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 44780d3..d75e314 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Aquest telèfon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Aquesta tauleta"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Aquest ordinador (intern)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Micròfon (intern)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base d\'altaveu"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositiu extern"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositiu connectat"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Has de reiniciar el teu dispositiu perquè s\'apliquin els canvis. Reinicia\'l ara o cancel·la."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculars amb cable"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculars amb cable"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Auriculars"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"Altaveu USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Connector per al micròfon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Micròfon USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activa"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactiva"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"S\'està canviant la xarxa de l\'operador de telefonia mòbil"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index cadb170..e775050 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (interní)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Reproduktor doku"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externí zařízení"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Připojené zařízení"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuto"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aby se tato změna projevila, je třeba zařízení restartovat. Restartujte zařízení nebo zrušte akci."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kabelová sluchátka"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofonu"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnout"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Vypnout"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Probíhá změna sítě operátora"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index d3b064c..a85bc29 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Denne tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (indbygget)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockhøjttaler"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhed"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Forbundet enhed"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at anvende denne ændring. Genstart nu, eller annuller."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Høretelefoner med ledning"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Stik til mikrofon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Til"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Fra"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Skift af mobilnetværk"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 50bc9cc..9ba5b50 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Dieses Smartphone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dieses Tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (intern)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock-Lautsprecher"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externes Gerät"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbundenes Gerät"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiviert"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Damit diese Änderung übernommen wird, musst du dein Gerät neu starten. Du kannst es jetzt neu starten oder den Vorgang abbrechen."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kabelgebundene Kopfhörer"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonanschluss"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB‑Mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"An"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Aus"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobilfunknetzwerk wird gewechselt"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index ea6b3ba..ce045a9 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Μόλις τώρα"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Αυτό το tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Μικρόφωνο (εσωτερικό)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Ηχείο βάσης σύνδεσης"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Εξωτερική συσκευή"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Συνδεδεμένη συσκευή"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Ενσύρματα ακουστικά"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Υποδοχή μικροφώνου"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Μικρόφωνο USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ενεργό"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ανενεργό"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Αλλαγή δικτύου εταιρείας κινητής τηλεφωνίας"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 03e505a..d4e01de 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microphone (internal)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphones"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB speaker"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mic"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 9fce566..602a0ed 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microphone (internal)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External Device"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphone"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB speaker"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mic"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Carrier network changing"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 03e505a..d4e01de 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microphone (internal)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphones"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB speaker"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mic"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 03e505a..d4e01de 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"This computer (internal)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microphone (internal)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphones"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB speaker"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mic"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 85cf936..0731b72 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎Just now‎‏‎‎‏‎"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‏‎This phone‎‏‎‎‏‎"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‎This tablet‎‏‎‎‏‎"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎This computer (internal)‎‏‎‎‏‎"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎Microphone (internal)‎‏‎‎‏‎"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‏‏‎‎Dock speaker‎‏‎‎‏‎"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‎‎External Device‎‏‎‎‏‎"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎Connected device‎‏‎‎‏‎"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎Disabled‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎Enabled‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎Your device must be rebooted for this change to apply. Reboot now or cancel.‎‏‎‎‏‎"</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎Wired headphone‎‏‎‎‏‎"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎‎Wired headphone‎‏‎‎‏‎"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎Headphone‎‏‎‎‏‎"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‎USB speaker‎‏‎‎‏‎"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‏‎Mic jack‎‏‎‎‏‎"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎USB mic‎‏‎‎‏‎"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎On‎‏‎‎‏‎"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎Off‎‏‎‎‏‎"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎Carrier network changing‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 3feee4c..b0d360d 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Micrófono (interno)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Bocina de la estación de carga"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Debes reiniciar el dispositivo para que se aplique el cambio. Reinícialo ahora o cancela la acción."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector para micrófono"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Micrófono USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activar"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivar"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio de proveedor de red"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index d2c7a02..716713a 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Micrófono (interno)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altavoz de la base"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reinicia ahora o cancela la acción."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector jack para micrófono"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Micrófono USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambiando la red del operador"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 1452a0e..f1975fa 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"See telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"See tahvelarvuti"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (sisemine)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doki kõlar"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Väline seade"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ühendatud seade"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Keelatud"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Selle muudatuse rakendamiseks tuleb seade taaskäivitada. Taaskäivitage kohe või tühistage."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Juhtmega kõrvaklapid"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoni pistikupesa"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Sees"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Väljas"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operaatori võrku muudetakse"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 2d97bbf..fc4157c 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -564,7 +564,7 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmak eta abisuak"</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, abisua, erlojua"</string>
-    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Ez molestatzeko modua"</string>
+    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Ez molestatzeko"</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>
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefono hau"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tableta hau"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofonoa (barnekoa)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Oinarri bozgorailuduna"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kanpoko gailua"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Konektatutako gailua"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aldaketa aplikatzeko, berrabiarazi egin behar da gailua. Berrabiaraz ezazu orain, edo utzi bertan behera."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Entzungailu kableduna"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonoaren konektorea"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB bidezko mikrofonoa"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktibatu"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desaktibatu"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operadorearen sarea aldatzen"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 11dcc99..3dc0e85 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"هم‌اکنون"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"این تلفن"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"این رایانه لوحی"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"این رایانه (داخلی)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"میکروفون (داخلی)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"بلندگوی پایه اتصال"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"دستگاه خارجی"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"دستگاه متصل"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"هدفون سیمی"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"هدفون سیمی"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"هدفون"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"‏بلندگوی USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"فیش میکروفون"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"‏میکروفون USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"روشن"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"خاموش"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"تغییر شبکه شرکت مخابراتی"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 4ee5c60..b92616b 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tämä puhelin"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tämä tabletti"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofoni (sisäinen)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Telinekaiutin"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ulkoinen laite"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Yhdistetty laite"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ei käytössä"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Laitteesi on käynnistettävä uudelleen, jotta muutos tulee voimaan. Käynnistä uudelleen nyt tai peru."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Langalliset kuulokkeet"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoniliitäntä"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-mikrofoni"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Päällä"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ei käytössä"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operaattorin verkko muuttuu"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index d975f3e..bf9adba 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microphone (interne)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur du socle"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Votre appareil doit être redémarré pour que ce changement prenne effet. Redémarrez-le maintenant ou annulez la modification."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Écouteurs filaires"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Prise du microphone"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Microphone USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activé"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Désactivé"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Changer de réseau de fournisseur de services"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index c987bc4..0df9489 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Micro (interne)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur station d\'accueil"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Vous devez redémarrer l\'appareil pour que cette modification soit appliquée. Redémarrez maintenant ou annulez l\'opération."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Casque filaire"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Connecteur micro"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Micro USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Allumé"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Éteint"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Modification du réseau de l\'opérateur"</string>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index e6d098f..8f7a8b6 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -26,10 +26,10 @@
     <item msgid="6050951078202663628">"Conectando..."</item>
     <item msgid="8356618438494652335">"Autenticando…"</item>
     <item msgid="2837871868181677206">"Obtendo enderezo IP..."</item>
-    <item msgid="4613015005934755724">"Conectada"</item>
+    <item msgid="4613015005934755724">"Conectado"</item>
     <item msgid="3763530049995655072">"Suspendida"</item>
     <item msgid="7852381437933824454">"Desconectando..."</item>
-    <item msgid="5046795712175415059">"Desconectada"</item>
+    <item msgid="5046795712175415059">"Desconectado"</item>
     <item msgid="2473654476624070462">"Incorrecta"</item>
     <item msgid="9146847076036105115">"Bloqueada"</item>
     <item msgid="4543924085816294893">"Evitando conexión deficiente temporalmente"</item>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 0216ad9..98a1401 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -510,7 +510,7 @@
     <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sen fíos"</string>
     <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Cargando"</string>
     <string name="battery_info_status_discharging" msgid="6962689305413556485">"Non se está cargando"</string>
-    <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Conectado, pero non cargando"</string>
+    <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Conectado, pero sen cargar"</string>
     <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
     <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Carga completa"</string>
     <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Carga en pausa"</string>
@@ -564,7 +564,7 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas e recordatorios"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación defina alarmas e planifique accións que dependan da hora. Con este permiso, a aplicación pode executarse en segundo plano, o que pode provocar un maior consumo de batería.\n\nSe este permiso está desactivado, non funcionarán as alarmas que xa se definisen nin os eventos que dependan da hora planificados por esta aplicación."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planificar, alarma, recordatorio, reloxo"</string>
-    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Modo Non molestar"</string>
+    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Non molestar"</string>
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Non molestar"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar modo Non molestar"</string>
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tableta"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Micrófono (interno)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altofalante da base"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necesario reiniciar o teu dispositivo para aplicar este cambio. Reiníciao agora ou cancela o cambio."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector do micrófono"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Micrófono USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activada"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivada"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio de rede do operador"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index bc34173..aa768f8 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"આ ફોન"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"આ ટૅબ્લેટ"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"આ કમ્પ્યૂટર (આંતરિક)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"માઇક્રોફોન (આંતરિક)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ડૉક સ્પીકર"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"બહારનું ડિવાઇસ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"કનેક્ટ કરેલું ડિવાઇસ"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"વાયરવાળો હૅડફોન"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"વાયરવાળો હૅડફોન"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"હૅડફોન"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB સ્પીકર"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"માઇક જૅક"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB માઇક"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ચાલુ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"બંધ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"કૅરીઅર નેટવર્કમાં ફેરફાર થઈ રહ્યો છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 25309b3..b8e55b5 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"अभी-अभी"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यह फ़ोन"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यह टैबलेट"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"इस कंप्यूटर पर (इंटरनल)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"माइक्रोफ़ोन (इंटरनल)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाहरी डिवाइस"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट किया गया डिवाइस"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"वायर वाला हेडफ़ोन"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तार वाला हेडफ़ोन"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफ़ोन"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"यूएसबी स्पीकर"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक्रोफ़ोन जैक"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"यूएसबी माइक्रोफ़ोन"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"चालू है"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"बंद है"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"मोबाइल और इंटरनेट सेवा देने वाली कंपनी का नेटवर्क बदल रहा है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 516834d..45ac8c2 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ovo računalo (interno)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (ugrađeni)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Uređaj se mora ponovno pokrenuti da bi se ta promjena primijenila. Ponovo pokrenite uređaj odmah ili odustanite."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žičane slušalice"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalice"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB zvučnik"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utičnica za mikrofon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključeno"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promjena mreže mobilnog operatera"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 1d2b715..62f4777 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ez a telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ez a táblagép"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (belső)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhangszóró"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Külső eszköz"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Csatlakoztatott eszköz"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Letiltva"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Az eszközt újra kell indítani, hogy a módosítás megtörténjen. Indítsa újra most, vagy vesse el a módosítást."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vezetékes fejhallgató"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jack csatlakozója"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Be"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ki"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Szolgáltatói hálózat váltása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 85b5548..ba4e204 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Այս հեռախոսը"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Այս պլանշետը"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Խոսափող (ներքին)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Դոկ-կայանով բարձրախոս"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Արտաքին սարք"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Միացված սարք"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Լարով ականջակալ"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Խոսափողի հարակցիչ"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB խոսափող"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Միացնել"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Անջատել"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Օպերատորի ցանցի փոփոխություն"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 6660188..89d82f0 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ponsel ini"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (internal)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker dok"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Perangkat Eksternal"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Perangkat yang terhubung"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Nonaktif"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Perangkat Anda harus di-reboot agar perubahan ini diterapkan. Reboot sekarang atau batalkan."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Headphone berkabel"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Colokan mikrofon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Mikrofon USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktif"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Nonaktif"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Jaringan operator berubah"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 0984892..b5d1831 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Þessi sími"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Þessi spjaldtölva"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Hljóðnemi (innbyggður)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Hátalaradokka"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ytra tæki"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tengt tæki"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slökkt"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Endurræsa þarf tækið til að þessi breyting taki gildi. Endurræstu núna eða hættu við."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Heyrnartól með snúru"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Hljóðnematengi"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-hljóðnemi"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Kveikt"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Slökkt"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Skiptir um farsímakerfi"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index e70c415..1436c17 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Questo smartphone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Questo tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microfono (interno)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base con altoparlante"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo esterno"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Per applicare questa modifica, devi riavviare il dispositivo. Riavvia ora o annulla."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Cuffie con cavo"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Jack per microfono"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Microfono USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio della rete dell\'operatore"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 662455d..986fcb9 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"הטלפון הזה"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"הטאבלט הזה"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"מיקרופון (פנימי)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"רמקול של אביזר העגינה"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"מכשיר חיצוני"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"המכשיר המחובר"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"אוזניות חוטיות"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"שקע למיקרופון"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"‏מיקרופון USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"פועלת"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"מצב כבוי"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"רשת ספק משתנה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 70fc2d0..185c6c9 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"たった今"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"このデバイス"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"このタブレット"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"このパソコン(内蔵)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"マイク(内蔵)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ホルダー スピーカー"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部デバイス"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"接続済みのデバイス"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有線ヘッドフォン"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線ヘッドフォン"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ヘッドフォン"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB スピーカー"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"マイク差込口"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB マイク"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ON"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"OFF"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"携帯通信会社のネットワークを変更します"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 040c046..9532c3a 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ეს ტელეფონი"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ამ ტაბლეტზე"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ეს კომპიუტერი (შიდა)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"მიკროფონი (შიდა)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"სამაგრის დინამიკი"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"გარე მოწყობილობა"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"დაკავშირებული მოწყობილობა"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"სადენიანი ყურსასმენი"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"სადენიანი ყურსასმენი"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ყურსასმენი"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB დინამიკი"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"მიკროფონის ჯეკი"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB მიკროფონი"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ჩართვა"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"გამორთვა"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ოპერატორის ქსელის შეცვლა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index f54067c..6789f40 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Осы телефон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Осы планшет"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Микрофон (ішкі)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Динамигі бар қондыру станциясы"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Сыртқы құрылғы"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Жалғанған құрылғы"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Сымды құлақаспап"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофон ұяшығы"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB микрофоны"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Қосу"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Өшіру"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Оператор желісін өзгерту"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 807e7e2..43807a0 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"អម្បាញ់មិញ"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ថេប្លេតនេះ"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"កុំព្យូទ័រនេះ (ខាងក្នុង)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"មីក្រូហ្វូន (ខាងក្នុង)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ឧបាល័រជើងទម្រ"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ឧបករណ៍ខាងក្រៅ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"​ឧបករណ៍ដែលបាន​ភ្ជាប់"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"កាស​មានខ្សែ"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"កាស​មានខ្សែ"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"កាស"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"ឧបករណ៍បំពងសំឡេង USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ឌុយ​មីក្រូហ្វូន"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"មីក្រូហ្វូន USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"បើក"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"បិទ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"បណ្តាញ​ក្រុមហ៊ុនសេវាទូរសព្ទ​កំពុងផ្លាស់ប្តូរ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 23e31c2..29417d6 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ಈ ಫೋನ್"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ಈ ಟ್ಯಾಬ್ಲೆಟ್"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"ಮೈಕ್ರೊಫೋನ್‌ (ಆಂತರಿಕ)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ಡಾಕ್ ಸ್ಪೀಕರ್"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ಬಾಹ್ಯ ಸಾಧನ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ಕನೆಕ್ಟ್ ಮಾಡಿರುವ ಸಾಧನ"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ವೈಯರ್ ಹೊಂದಿರುವ ಹೆಡ್‌ಫೋನ್"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ಮೈಕ್‌ ಜ್ಯಾಕ್‌"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB ಮೈಕ್‌"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ಆನ್ ಆಗಿದೆ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ಆಫ್"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ವಾಹಕ ನೆಟ್‌ವರ್ಕ್ ಬದಲಾಯಿಸುವಿಕೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index cfbddad..4b0cfb4 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"조금 전"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"이 휴대전화"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"이 태블릿"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"마이크(내부)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"도크 스피커"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"외부 기기"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"연결된 기기"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"유선 헤드폰"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"마이크 잭"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB 마이크"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"사용"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"사용 안 함"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"이동통신사 네트워크 변경"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index c5a3bcc..17b55c4 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ушул телефон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ушул планшет"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Микрофон (ички)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док бекеттин динамиги"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Тышкы түзмөк"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Туташкан түзмөк"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Зымдуу гарнитура"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофондун оюкчасы"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Күйгүзүү"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Өчүрүү"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Байланыш оператору өзгөртүлүүдө"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 70fd196..e6e8ef8 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ແທັບເລັດນີ້"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"ໄມໂຄຣໂຟນ (ພາຍໃນ)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ແທ່ນວາງລຳໂພງ"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ອຸປະກອນພາຍນອກ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ອຸປະກອນທີ່ເຊື່ອມຕໍ່"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ຫູຟັງແບບມີສາຍ"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ຊ່ອງສຽງໄມ"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"ໄມ USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ເປີດ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ປິດ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ການປ່ຽນເຄືອຂ່າຍຜູ້ໃຫ້ບໍລິການ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 5488437..5945e2a 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis telefonas"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetinis kompiuteris"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofonas (vidinis)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doko garsiakalbis"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Išorinis įrenginys"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Prijungtas įrenginys"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Išjungta"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kad pakeitimas būtų pritaikytas, įrenginį reikia paleisti iš naujo. Dabar paleiskite iš naujo arba atšaukite."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Laidinės ausinės"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofono jungtis"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofonas"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Įjungta"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Išjungta"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Keičiamas operatoriaus tinklas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index faf7b5f..34f6c4c 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis tālrunis"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetdators"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofons (iebūvētais)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doka skaļrunis"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ārēja ierīce"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pievienotā ierīce"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Atspējots"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Lai šīs izmaiņas tiktu piemērotas, nepieciešama ierīces atkārtota palaišana. Atkārtoti palaidiet to tūlīt vai atceliet izmaiņas."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vadu austiņas"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofona ligzda"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofons"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ieslēgts"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Izslēgts"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobilo sakaru operatora tīkla mainīšana"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 2f215e8..ea9ff6c 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Пред малку"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овој телефон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овој таблет"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Овој компјуер (внатрешен)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Микрофон (внатрешен)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док со звучник"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Надворешен уред"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Поврзан уред"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Жичени слушалки"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичени слушалки"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалка"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB-звучник"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Приклучок за микрофон"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вклучено"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Исклучено"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Променување на мрежата на операторот"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 859ac3b..7af53aa 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ഈ ഫോൺ"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ഈ ടാബ്‌ലെറ്റ്"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ഈ കമ്പ്യൂട്ടർ (ഇന്റേണൽ)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"മൈക്രോഫോൺ (ഇന്റേണൽ)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ഡോക്ക് സ്‌പീക്കർ"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ബാഹ്യ ഉപകരണം"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"കണക്‌റ്റ് ചെയ്‌ത ഉപകരണം"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"വയേർഡ് ഹെഡ്ഫോൺ"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"വയേർഡ് ഹെഡ്ഫോൺ"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ഹെഡ്ഫോൺ"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB സ്‌പീക്കർ"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"മൈക്ക് ജാക്ക്"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB മൈക്ക്"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ഓണാണ്"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ഓഫാണ്"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"കാരിയർ നെറ്റ്‌വർക്ക് മാറ്റൽ"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 7615dcc..bc953d3 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Дөнгөж сая"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Энэ утас"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Энэ таблет"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Микрофон (дотоод)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Суурилуулагчийн чанга яригч"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Гадаад төхөөрөмж"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Холбогдсон төхөөрөмж"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Утастай чихэвч"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофоны чихэвчний оролт"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Асаах"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Унтраах"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Оператор компанийн сүлжээг өөрчилж байна"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index fb90ce3..581545d 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"हा फोन"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"हा टॅबलेट"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"हा काँप्युटर (अंतर्गत)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"मायक्रोफोन (अंतर्गत)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिव्हाइस"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट केलेले डिव्हाइस"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"वायर असलेला हेडफोन"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"वायर्ड हेडफोन"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफोन"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB स्पीकर"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक जॅक"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB माइक"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"सुरू करा"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"बंद करा"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"वाहक नेटवर्क बदलत आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index acd8487..7990e04 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Sebentar tadi"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefon ini"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Komputer ini (dalaman)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (dalaman)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Pembesar suara dok"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Peranti Luar"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Peranti yang disambungkan"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dilumpuhkan"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Peranti anda mesti dibut semula supaya perubahan ini berlaku. But semula sekarang atau batalkan."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Fon kepala berwayar"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fon kepala berwayar"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fon kepala"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"Pembesar suara USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Bicu mikrofon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Mikrofon USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Hidup"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Mati"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rangkaian pembawa berubah"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 2640c3a..063fc9c 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ဤဖုန်း"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ဤတက်ဘလက်"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"မိုက်ခရိုဖုန်း (စက်တွင်း)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"အထိုင် စပီကာ"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ပြင်ပစက်"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ချိတ်ဆက်ကိရိယာ"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ကြိုးတပ်နားကြပ်"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"မိုက်ဂျက်ပင်"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB မိုက်"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ဖွင့်"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ပိတ်"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ဝန်ဆောင်မှုပေးသူ ကွန်ရက် ပြောင်းလဲနေသည်။"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 4a53090..9036c8c 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefonen"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dette nettbrettet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (intern)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhøyttaler"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhet"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tilkoblet enhet"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slått av"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten din må startes på nytt for at denne endringen skal tre i kraft. Start på nytt nå eller avbryt."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"hodetelefoner med kabel"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonkontakt"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Av"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Bytting av operatørnettverk"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index a04028f..916aba9 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यो फोन"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यो ट्याब्लेट"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"यो कम्प्युटर (आन्तरिक)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"माइक्रोफोन (आन्तरिक)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डक स्पिकर"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिभाइस"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट गरिएको डिभाइस"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"तारसहितको हेडफोन"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तारयुक्त हेडफोन"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"हेडफोन"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB स्पिकर"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइकको ज्याक"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB माइक"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"अन छ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"अफ छ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"सेवा प्रदायकको नेटवर्क परिवर्तन गर्ने आइकन"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index c3998c5..78dfd35 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -577,15 +577,15 @@
     <string name="alarm_template_far" msgid="6382760514842998629">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duur"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Altijd vragen"</string>
-    <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat je uitzet"</string>
+    <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat je ze uitzet"</string>
     <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Geen naam)"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Zojuist"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Deze telefoon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Deze tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Deze computer (intern)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microfoon (intern)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockspeaker"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern apparaat"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbonden apparaat"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Uit"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aan"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Je apparaat moet opnieuw worden opgestart om deze wijziging toe te passen. Start nu opnieuw op of annuleer de wijziging."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Bedrade koptelefoon"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Bedrade koptelefoon"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Koptelefoon"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB-speaker"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Microfoonaansluiting"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-microfoon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Uit"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Netwerk van provider wordt gewijzigd"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 676a426..1b2e421 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ଏହି ଫୋନ"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ଏହି ଟାବଲେଟ"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"ମାଇକ୍ରୋଫୋନ (ଇଣ୍ଟର୍ନଲ)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ଡକ ସ୍ପିକର"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"କନେକ୍ଟ କରାଯାଇଥିବା ଡିଭାଇସ"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ତାରଯୁକ୍ତ ହେଡଫୋନ୍"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ମାଇକ ଜେକ"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB ମାଇକ"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ଚାଲୁ ଅଛି"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ବନ୍ଦ ଅଛି"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"କେରିଅର୍‍ ନେଟ୍‌ୱର୍କ ବଦଳୁଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 4ef2308c..c668e12 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ਇਹ ਟੈਬਲੈੱਟ"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ (ਅੰਦਰੂਨੀ)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ਡੌਕ ਸਪੀਕਰ"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ਕਨੈਕਟ ਕੀਤਾ ਡੀਵਾਈਸ"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ਤਾਰ ਵਾਲੇ ਹੈੱਡਫ਼ੋਨ"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ਮਾਈਕ ਜੈਕ"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB ਮਾਈਕ"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ਚਾਲੂ"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ਬੰਦ"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"ਕੈਰੀਅਰ ਨੈੱਟਵਰਕ ਦੀ ਬਦਲੀ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 3693c38..bb8fe2e 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ten telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ten tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ten komputer (wewnętrzny)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (wewnętrzny)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Głośnik ze stacją dokującą"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Urządzenie zewnętrzne"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Połączone urządzenie"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Wyłączono"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Wprowadzenie zmiany wymaga ponownego uruchomienia urządzenia. Uruchom ponownie teraz lub anuluj."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Słuchawki przewodowe"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Słuchawki przewodowe"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Słuchawki"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"Głośnik USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Gniazdo mikrofonu"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Mikrofon USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Włączono"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Wyłączono"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Zmiana sieci operatora"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 75ffcd1..7f5bb0f 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Este computador (interno)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microfone (interno)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Fones de ouvido com fio"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fone de ouvido"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"Alto-falante USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Microfone USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desativado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Alteração de rede da operadora"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 535261b..92ab714 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telemóvel"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microfone (interno)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altifalante estação carregamento"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo associado"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativada"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reiniciar o dispositivo para aplicar esta alteração. Reinicie agora ou cancele."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auscultadores com fios"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Microfone USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ligado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desligado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rede do operador em mudança."</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 75ffcd1..7f5bb0f 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Este computador (interno)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microfone (interno)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Fones de ouvido com fio"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Fone de ouvido"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"Alto-falante USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Microfone USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desativado"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Alteração de rede da operadora"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 242127e..e710634 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Acest telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Această tabletă"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Microfon (intern)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Difuzorul dispozitivului de andocare"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispozitiv extern"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispozitiv conectat"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dezactivat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să repornești dispozitivul. Repornește-l acum sau anulează."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Căști cu fir"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mufă pentru microfon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Microfon USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activat"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Dezactivat"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Se schimbă rețeaua operatorului"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 2ac0128..1cdef05 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Этот смартфон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Этот планшет"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Встроенный динамик компьютера"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Микрофон (встроенный)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Колонка с док-станцией"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Внешнее устройство"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Подключенное устройство"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Проводные наушники"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Проводные наушники"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Наушники"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB-колонка"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофонный разъем"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вкл."</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Выкл."</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Сменить сеть"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index f8565c4..980f9c8 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"මේ දැන්"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"මෙම දුරකථනය"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"මෙම ටැබ්ලටය"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"මෙම පරිගණකය (අභ්‍යන්තර)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"මයික්‍රෆෝනය (අභ්‍යන්තර)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ඩොක් ස්පීකරය"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"බාහිර උපාංගය"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"සම්බන්ධ කළ උපාංගය"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"රැහැන්ගත කළ හෙඩ්ෆෝන්"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"රැහැන්ගත හෙඩ්ෆෝන්"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"හෙඩ්ෆෝන්"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB ස්පීකරය"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"මයික් ජැක්කුව"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB මයික්"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ක්‍රියාත්මකයි"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ක්‍රියාවිරහිතයි"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"වාහක ජාලය වෙනස් වෙමින්"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 00bb994..ddb35d6 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefón"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofón (vnútorný)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Reproduktor doku"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externé zariadenie"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pripojené zariadenie"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Zmena sa prejaví až po reštarte zariadenia. Môžete ho teraz reštartovať alebo akciu zrušiť."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Slúchadlá s káblom"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofónu"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Mikrofón USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnúť"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Vypnúť"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mení sa sieť operátora"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 5bce9fa..2ee1241 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ta telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ta tablični računalnik"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Ta računalnik (notranji)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (notranji)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvočnik nosilca"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Zunanja naprava"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezana naprava"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogočeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Napravo je treba znova zagnati, da bo ta sprememba uveljavljena. Znova zaženite zdaj ali prekličite."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Žične slušalke"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žične slušalke"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Slušalke"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"Zvočnik USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Vtič za mikrofon"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Mikrofon USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vklop"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Izklop"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Spreminjanje omrežja operaterja"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 59472e3..d99f2c8 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ky tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofoni (i brendshëm)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altoparlanti i stacionit"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pajisja e lidhur"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pajisja jote duhet të riniset që ky ndryshim të zbatohet. Rinise tani ose anuloje."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kufje me tela"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Fisha e mikrofonit"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Mikrofoni me USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktive"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Joaktive"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rrjeti i operatorit celular po ndryshohet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 57e2a0a..34ccdaf 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Управо"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овај телефон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овај таблет"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Овај рачунар (интерно)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Микрофон (интерни)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Звучник базне станице"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Спољни уређај"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Повезани уређај"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Жичане слушалице"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичане слушалице"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Слушалице"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB звучник"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Утикач за микрофон"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB микрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Укључено"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Искључено"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Промена мреже мобилног оператера"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index a3eaee1..6b76f87 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Den här telefonen"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Den här surfplattan"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (inbyggd)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockningsstationens högtalare"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern enhet"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ansluten enhet"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inaktiverat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten måste startas om för att ändringen ska börja gälla. Starta om nu eller avbryt."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Hörlurar med sladd"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonuttag"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Av"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Byter leverantörsnätverk"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 9979a9d..f113e62 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Sasa hivi"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Simu hii"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Kishikwambi hiki"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Maikrofoni (ya ndani)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Spika ya kituo"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kifaa cha Nje"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Kifaa kilichounganishwa"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Vipokea sauti vya waya"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Pini ya maikrofoni"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Maikrofoni ya USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Umewashwa"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Umezimwa"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mabadiliko katika mtandao wa mtoa huduma"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 18a3186..beca738 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"இந்த மொபைல்"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"இந்த டேப்லெட்"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"மைக்ரோஃபோன் (அகம்)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"டாக் ஸ்பீக்கர்"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"வெளிப்புறச் சாதனம்"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"இணைக்கப்பட்டுள்ள சாதனம்"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"வயருள்ள ஹெட்ஃபோன்"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"மைக் ஜாக்"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB மைக்"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ஆன்"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ஆஃப்"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"மொபைல் நிறுவன நெட்வொர்க்கை மாற்றும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index f883796..56d0a7d 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ఈ ఫోన్"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ఈ టాబ్లెట్"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"ఈ కంప్యూటర్ (ఇంటర్నల్)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"మైక్రోఫోన్ (అంతర్గతం)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"డాక్ స్పీకర్"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ఎక్స్‌టర్నల్ పరికరం"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"కనెక్ట్ చేసిన పరికరం"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"వైర్ ఉన్న హెడ్‌ఫోన్"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"వైర్ ఉన్న హెడ్‌ఫోన్"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"హెడ్‌ఫోన్"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB స్పీకర్"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"మైక్ జాక్"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB మైక్"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ఆన్‌లో ఉంది"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ఆఫ్‌లో ఉంది"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"క్యారియర్ నెట్‌వర్క్ మారుతోంది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index e56656b..28a0d64 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"เมื่อสักครู่"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"แท็บเล็ตเครื่องนี้"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"คอมพิวเตอร์เครื่องนี้ (ภายใน)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"ไมโครโฟน (ภายใน)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"แท่นชาร์จที่มีลำโพง"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"อุปกรณ์ภายนอก"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"อุปกรณ์ที่เชื่อมต่อ"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"หูฟังแบบมีสาย"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"หูฟังแบบใช้สาย"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"หูฟัง"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"ลำโพง USB"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ช่องเสียบไมค์"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"ไมค์ USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"เปิด"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ปิด"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"การเปลี่ยนเครือข่ายผู้ให้บริการ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 3e9f238..926ce8f 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Ngayon lang"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ang teleponong ito"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ang tablet na ito"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"Sa computer na ito (internal)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikropono (internal)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker ng dock"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External na Device"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Nakakonektang device"</string>
@@ -686,11 +686,11 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Naka-disable"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Dapat i-reboot ang iyong device para mailapat ang pagbabagong ito. Mag-reboot ngayon o kanselahin."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired na headphone"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired na headphone"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"Headphone"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"USB speaker"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Jack ng mikropono"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB na mikropono"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Naka-on"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Naka-off"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Nagpapalit ng carrier network"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 22c0ab1..d7ad6b1 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu tablet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (dahili)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Yuva hoparlörü"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Harici Cihaz"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Bağlı cihaz"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazınızın yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Kablolu kulaklık"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jakı"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mikrofon"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Açık"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Kapalı"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operatör ağı değiştiriliyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 50ebd24..3ccbcf2 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -287,8 +287,8 @@
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Дозволити розблокування завантажувача"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Дозволити виробникові розблоковувати пристрій?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ЗАСТЕРЕЖЕННЯ. Функції захисту пристрою не працюватимуть, поки ввімкнено це налаштування."</string>
-    <string name="mock_location_app" msgid="6269380172542248304">"Вибрати додаток для фіктивних місцезнаходжень"</string>
-    <string name="mock_location_app_not_set" msgid="6972032787262831155">"Додаток для фіктивних місцезнаходжень не вибрано"</string>
+    <string name="mock_location_app" msgid="6269380172542248304">"Вибрати додаток для фіктивних місцеположень"</string>
+    <string name="mock_location_app_not_set" msgid="6972032787262831155">"Додаток для фіктивних місцеположень не вибрано"</string>
     <string name="mock_location_app_set" msgid="4706722469342913843">"Додаток для фіктивних місцезнаходжень: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"Мережі"</string>
     <string name="wifi_display_certification" msgid="1805579519992520381">"Сертифікація бездрот. екрана"</string>
@@ -577,15 +577,16 @@
     <string name="alarm_template_far" msgid="6382760514842998629">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Тривалість"</string>
     <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Запитувати щоразу"</string>
-    <string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string>
+    <string name="zen_mode_forever" msgid="3339224497605461291">"Доки ви не вимкнете"</string>
     <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Без імені)"</string>
     <string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Цей телефон"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Цей планшет"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Мікрофон (внутрішній)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Динамік док-станції"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Зовнішній пристрій"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Підключений пристрій"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Дротові навушники"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Гніздо для мікрофона"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB-мікрофон"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Увімкнено"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Вимкнено"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Змінення мережі оператора"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index d6dd2c8..2157f63 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -582,10 +582,10 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"یہ فون"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"یہ ٹیبلیٹ"</string>
+    <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"یہ کمپیوٹر (داخلی)"</string>
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"مائیکروفون (داخلی)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ڈاک اسپیکر"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"بیرونی آلہ"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"منسلک آلہ"</string>
@@ -686,11 +686,11 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"وائرڈ ہیڈ فون"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
-    <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
-    <skip />
+    <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"تار والا ہیڈ فون"</string>
+    <string name="media_transfer_headphone_name" msgid="1131962659136578852">"ہیڈ فون"</string>
+    <string name="media_transfer_usb_speaker_name" msgid="4736537022543593896">"‏‫USB اسپیکر"</string>
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مائیک جیک"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"‏‫USB مائیک"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"آن"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"آف"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"کیریئر نیٹ ورک کی تبدیلی"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 1e3d64f..40d1d10 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Hozir"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Shu telefon"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Shu planshet"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Mikrofon (ichki)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok-stansiyali karnay"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Tashqi qurilma"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ulangan qurilma"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Oʻchiq"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar kuchga kirishi uchun qurilmani oʻchirib yoqing. Buni hozir yoki keyinroq bajarishingiz mumkin."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Simli quloqlik"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon ulagichi"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB mik"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Yoniq"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Oʻchiq"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobil tarmoqni o‘zgartirish"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 95092ff..874aa18 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Vừa xong"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Điện thoại này"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Máy tính bảng này"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Micrô (bên trong)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Loa có gắn đế"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Thiết bị bên ngoài"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Thiết bị đã kết nối"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bạn phải khởi động lại thiết bị để áp dụng sự thay đổi này. Hãy khởi động lại ngay hoặc hủy."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Tai nghe có dây"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Giắc cắm micrô"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"Micrô có cổng USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Đang bật"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Đang tắt"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Thay đổi mạng của nhà mạng"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index fb9e7b1..8edc656 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"这部手机"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"这部平板电脑"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"麦克风(内部)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"基座音箱"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"连接的设备"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有线耳机"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麦克风插孔"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB 麦克风"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"开启"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"关闭"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"运营商网络正在更改"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 9c6da5f..736a30d 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -100,7 +100,7 @@
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"使用中。右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 電量。"</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>,右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 電量。"</string>
+    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"左:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>,右:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 電量。"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"左側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 電量"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"右側:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 電量。"</string>
     <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"左耳機:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"此平板電腦"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"麥克風 (內置)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"插座喇叭"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連接的裝置"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有線耳機"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB 麥克風"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"關閉"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"流動網絡供應商網絡正在變更"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 501b088..692b6e1 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"這支手機"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"這台平板電腦"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"麥克風 (內部)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"座架喇叭"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連結的裝置"</string>
@@ -686,11 +687,14 @@
     <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="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"有線耳機"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"USB 麥克風"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"關閉"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"電信業者網路正在進行變更"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 4f875de..4f0acbd 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -582,10 +582,11 @@
     <string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string>
     <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Le foni"</string>
     <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Le thebhulethi"</string>
+    <!-- no translation found for media_transfer_this_device_name_desktop (7912386128141470452) -->
+    <skip />
     <!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
     <skip />
-    <!-- no translation found for media_transfer_internal_mic (797333824290228595) -->
-    <skip />
+    <string name="media_transfer_internal_mic" msgid="797333824290228595">"Imakrofoni (okwangaphakathi)"</string>
     <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Isipikha sentuba"</string>
     <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Idivayisi Yangaphandle"</string>
     <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Idivayisi exhunyiwe"</string>
@@ -686,11 +687,14 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ikhutshaziwe"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kufanele idivayisi yakho iqaliswe ukuze lolu shintsho lusebenze. Qalisa manje noma khansela."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Ama-headphone anentambo"</string>
-    <!-- no translation found for media_transfer_wired_device_mic_name (7417067197803840965) -->
+    <!-- no translation found for media_transfer_wired_headphone_name (8698668536022665254) -->
     <skip />
-    <!-- no translation found for media_transfer_usb_device_mic_name (9189914846215516322) -->
+    <!-- no translation found for media_transfer_headphone_name (1131962659136578852) -->
     <skip />
+    <!-- no translation found for media_transfer_usb_speaker_name (4736537022543593896) -->
+    <skip />
+    <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Umgodi we-earphone ye-mic"</string>
+    <string name="media_transfer_usb_device_mic_name" msgid="9189914846215516322">"I-mic ye-USB"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vuliwe"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Valiwe"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Inethiwekhi yenkampani yenethiwekhi iyashintsha"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 34e33c0..efc98db 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1645,13 +1645,13 @@
     <string name="media_transfer_headphone_name">Headphone</string>
 
     <!-- Name of the usb audio device speaker, used in desktop devices. [CHAR LIMIT=50] -->
-    <string name="media_transfer_usb_speaker_name">USB speaker</string>
+    <string name="media_transfer_usb_audio_name">USB audio</string>
 
     <!-- Name of the 3.5mm audio device mic. [CHAR LIMIT=50] -->
     <string name="media_transfer_wired_device_mic_name">Mic jack</string>
 
     <!-- Name of the usb audio device mic. [CHAR LIMIT=50] -->
-    <string name="media_transfer_usb_device_mic_name">USB mic</string>
+    <string name="media_transfer_usb_device_mic_name">USB microphone</string>
 
     <!-- Label for Wifi hotspot switch on. Toggles hotspot on [CHAR LIMIT=30] -->
     <string name="wifi_hotspot_switch_on_text">On</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index ff00fb3..c634216 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -33,6 +33,8 @@
 import android.text.format.DateFormat;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import java.io.BufferedReader;
@@ -153,13 +155,19 @@
         return null;
     }
 
-    public static String getSecurityPatch() {
+    /** Returns security patch in default locale. */
+    public static @Nullable String getSecurityPatch() {
+        return getSecurityPatch(Locale.getDefault());
+    }
+
+    /** Returns security patch in given locale. */
+    public static @Nullable String getSecurityPatch(@NonNull Locale locale) {
         String patch = Build.VERSION.SECURITY_PATCH;
         if (!"".equals(patch)) {
             try {
                 SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd");
                 Date patchDate = template.parse(patch);
-                String format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "dMMMMyyyy");
+                String format = DateFormat.getBestDateTimePattern(locale, "dMMMMyyyy");
                 patch = DateFormat.format(format, patchDate).toString();
             } catch (ParseException e) {
                 // broken parse; fall through and use the raw string
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index e41126f..2475c8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -31,6 +31,8 @@
 
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
+import com.android.settingslib.widget.SettingsThemeHelper;
+import com.android.settingslib.widget.theme.R;
 
 /**
  * A custom preference that provides inline switch toggle. It has a mandatory field for title, and
@@ -62,7 +64,9 @@
 
     @Override
     protected int getSecondTargetResId() {
-        return androidx.preference.R.layout.preference_widget_switch_compat;
+        return SettingsThemeHelper.isExpressiveTheme(getContext())
+                ? R.layout.settingslib_expressive_preference_switch
+                : androidx.preference.R.layout.preference_widget_switch_compat;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
index 58dc8c7..e7c7476 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
@@ -45,6 +45,7 @@
             DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
             DeviceSettingId.DEVICE_SETTING_ID_DEVICE_DETAILS_FOOTER,
             DeviceSettingId.DEVICE_SETTING_ID_ANC,
+            DeviceSettingId.DEVICE_SETTING_ID_GENERAL_BLUETOOTH_DEVICE_HEADER,
         },
         open = true)
 public @interface DeviceSettingId {
@@ -114,6 +115,9 @@
     /** Device setting ID for "More Settings" page. */
     int DEVICE_SETTING_ID_MORE_SETTINGS = 21;
 
+    /** Device setting ID for general bluetooth device header. */
+    int DEVICE_SETTING_ID_GENERAL_BLUETOOTH_DEVICE_HEADER = 22;
+
     /** Device setting ID for ANC. */
     int DEVICE_SETTING_ID_ANC = 1001;
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
index 38183d5..da01b3b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
@@ -32,9 +32,9 @@
  */
 data class DeviceSettingItem(
     @DeviceSettingId val settingId: Int,
-    val packageName: String,
-    val className: String,
-    val intentAction: String,
+    val packageName: String? = null,
+    val className: String? = null,
+    val intentAction: String? = null,
     val preferenceKey: String? = null,
     val highlighted: Boolean = false,
     val extras: Bundle = Bundle.EMPTY,
@@ -62,11 +62,11 @@
                     parcel.run {
                         DeviceSettingItem(
                             settingId = readInt(),
-                            packageName = readString() ?: "",
-                            className = readString() ?: "",
-                            intentAction = readString() ?: "",
+                            packageName = readString(),
+                            className = readString(),
+                            intentAction = readString(),
                             highlighted = readBoolean(),
-                            preferenceKey = readString() ?: "",
+                            preferenceKey = readString(),
                             extras = readBundle((Bundle::class.java.classLoader)) ?: Bundle.EMPTY,
                         )
                     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatus.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatus.aidl
new file mode 100644
index 0000000..d837806
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.settingslib.bluetooth.devicesettings;
+
+parcelable DeviceSettingsConfigServiceStatus;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatus.kt
new file mode 100644
index 0000000..ae86771
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatus.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.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * A data class representing a device settings config service status.
+ *
+ * @property success Whether the status is succeed.
+ * @property extras Extra bundle
+ */
+data class DeviceSettingsConfigServiceStatus(
+    val success: Boolean,
+    val extras: Bundle = Bundle.EMPTY,
+) : Parcelable {
+
+    override fun describeContents(): Int = 0
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.run {
+            writeBoolean(success)
+            writeBundle(extras)
+        }
+    }
+
+    companion object {
+        @JvmField
+        val CREATOR: Parcelable.Creator<DeviceSettingsConfigServiceStatus> =
+            object : Parcelable.Creator<DeviceSettingsConfigServiceStatus> {
+                override fun createFromParcel(parcel: Parcel) =
+                    parcel.run {
+                        DeviceSettingsConfigServiceStatus(
+                            success = readBoolean(),
+                            extras = readBundle((Bundle::class.java.classLoader)) ?: Bundle.EMPTY,
+                        )
+                    }
+
+                override fun newArray(size: Int): Array<DeviceSettingsConfigServiceStatus?> {
+                    return arrayOfNulls(size)
+                }
+            }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
index 977849e..77d790e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
@@ -21,7 +21,7 @@
 import android.os.Parcelable
 
 /**
- * A data class representing a device settings item in bluetooth device details config.
+ * A data class representing a device settings provider service status.
  *
  * @property enabled Whether the service is enabled.
  * @property extras Extra bundle
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
index 647611e..9cf4907 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsConfigProviderService.aidl
@@ -17,8 +17,8 @@
 package com.android.settingslib.bluetooth.devicesettings;
 
 import com.android.settingslib.bluetooth.devicesettings.DeviceInfo;
-import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig;
+import com.android.settingslib.bluetooth.devicesettings.IGetDeviceSettingsConfigCallback;
 
 interface IDeviceSettingsConfigProviderService {
-   DeviceSettingsConfig getDeviceSettingsConfig(in DeviceInfo device);
+   oneway void getDeviceSettingsConfig(in DeviceInfo device, in IGetDeviceSettingsConfigCallback callback);
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IGetDeviceSettingsConfigCallback.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IGetDeviceSettingsConfigCallback.aidl
new file mode 100644
index 0000000..403cdd9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IGetDeviceSettingsConfigCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.settingslib.bluetooth.devicesettings;
+
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig;
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfigServiceStatus;
+
+interface IGetDeviceSettingsConfigCallback {
+   oneway void onResult(in DeviceSettingsConfigServiceStatus status, in DeviceSettingsConfig config) = 0;
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index 3d8ff86..4af0504 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -33,12 +33,15 @@
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingPreferenceState
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfigServiceStatus
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
+import com.android.settingslib.bluetooth.devicesettings.IGetDeviceSettingsConfigCallback
 import com.android.settingslib.bluetooth.devicesettings.data.model.ServiceConnectionStatus
 import java.util.concurrent.ConcurrentHashMap
 import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -63,6 +66,7 @@
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.plus
+import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -74,22 +78,22 @@
     private val backgroundCoroutineContext: CoroutineContext,
 ) {
     data class EndPoint(
-        private val packageName: String,
+        private val packageName: String?,
         private val className: String?,
-        private val intentAction: String,
+        private val intentAction: String?,
     ) {
-        fun toIntent(): Intent =
-            Intent().apply {
+        fun toIntent(): Intent? {
+            if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(intentAction)) {
+                return null
+            }
+            return Intent().apply {
                 if (className.isNullOrBlank()) {
                     setPackage(packageName)
                 } else {
-                    setClassName(packageName, className)
+                    setClassName(packageName!!, className)
                 }
                 setAction(intentAction)
             }
-
-        fun isValid(): Boolean {
-            return !TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(intentAction)
         }
     }
 
@@ -126,8 +130,9 @@
                     when (it) {
                         is ServiceConnectionStatus.Connected ->
                             flowOf(
-                                it.service.getDeviceSettingsConfig(
-                                    deviceInfo { setBluetoothAddress(cachedDevice.address) }
+                                getDeviceSettingsConfigFromService(
+                                    deviceInfo { setBluetoothAddress(cachedDevice.address) },
+                                    it.service,
                                 )
                             )
                         ServiceConnectionStatus.Connecting -> flowOf()
@@ -137,6 +142,27 @@
                 .first()
         }
 
+    private suspend fun getDeviceSettingsConfigFromService(
+        deviceInfo: DeviceInfo,
+        service: IDeviceSettingsConfigProviderService,
+    ): DeviceSettingsConfig? = suspendCancellableCoroutine { continuation ->
+        service.getDeviceSettingsConfig(
+            deviceInfo,
+            object : IGetDeviceSettingsConfigCallback.Stub() {
+                override fun onResult(
+                    status: DeviceSettingsConfigServiceStatus,
+                    config: DeviceSettingsConfig?,
+                ) {
+                    if (!status.success) {
+                        continuation.resume(null)
+                    } else {
+                        continuation.resume(config)
+                    }
+                }
+            },
+        )
+    }
+
     private val settingIdToItemMapping =
         flow {
                 if (!isServiceEnabled.await()) {
@@ -160,6 +186,12 @@
             }
             .shareIn(scope = coroutineScope, started = SharingStarted.WhileSubscribed(), replay = 1)
 
+    private val services =
+        ConcurrentHashMap<
+            EndPoint,
+            StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>,
+        >()
+
     /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
     suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? {
         if (!isServiceEnabled.await()) {
@@ -222,24 +254,23 @@
                     )
                 }
             }
-            ?.filter { it.isValid() }
             ?.distinct()
-            ?.associateBy(
-                { it },
-                { endpoint ->
-                    services.computeIfAbsent(endpoint) {
-                        getService(
-                                endpoint.toIntent(),
-                                IDeviceSettingsProviderService.Stub::asInterface,
-                            )
-                            .stateIn(
-                                coroutineScope.plus(backgroundCoroutineContext),
-                                SharingStarted.WhileSubscribed(),
-                                ServiceConnectionStatus.Connecting,
-                            )
-                    }
-                },
-            )
+            ?.mapNotNull { endpoint ->
+                endpoint.toIntent()?.let { intent ->
+                    Pair(
+                        endpoint,
+                        services.computeIfAbsent(endpoint) {
+                            getService(intent, IDeviceSettingsProviderService.Stub::asInterface)
+                                .stateIn(
+                                    coroutineScope.plus(backgroundCoroutineContext),
+                                    SharingStarted.WhileSubscribed(),
+                                    ServiceConnectionStatus.Connecting,
+                                )
+                        },
+                    )
+                }
+            }
+            ?.toMap()
 
     private fun getDeviceSettingsFromService(
         cachedDevice: CachedBluetoothDevice,
@@ -320,11 +351,5 @@
         const val CONFIG_SERVICE_PACKAGE_NAME = "DEVICE_SETTINGS_CONFIG_PACKAGE_NAME"
         const val CONFIG_SERVICE_CLASS_NAME = "DEVICE_SETTINGS_CONFIG_CLASS"
         const val CONFIG_SERVICE_INTENT_ACTION = "DEVICE_SETTINGS_CONFIG_ACTION"
-
-        val services =
-            ConcurrentHashMap<
-                EndPoint,
-                StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>,
-            >()
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
index ef0f6cb..13a0601 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
@@ -42,6 +42,8 @@
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
 
+import java.util.Objects;
+
 /**
  * Drawable displaying a mobile cell signal indicator.
  */
@@ -90,6 +92,10 @@
     private int mCurrentDot;
 
     public SignalDrawable(Context context) {
+        this(context, new Handler());
+    }
+
+    public SignalDrawable(@NonNull Context context, @NonNull Handler handler) {
         super(context.getDrawable(ICON_RES));
         final String attributionPathString = context.getString(
                 com.android.internal.R.string.config_signalAttributionPath);
@@ -106,7 +112,7 @@
         mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size);
         mTransparentPaint.setColor(context.getColor(android.R.color.transparent));
         mTransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
-        mHandler = new Handler();
+        mHandler = handler;
         setDarkIntensity(0);
     }
 
@@ -304,6 +310,17 @@
                 | level;
     }
 
+    @Override
+    public boolean equals(@Nullable Object other) {
+        return other instanceof SignalDrawable
+                && ((SignalDrawable) other).getLevel() == this.getLevel();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getLevel());
+    }
+
     /** Returns the state representing empty mobile signal with the given number of levels. */
     public static int getEmptyState(int numLevels) {
         return getState(0, numLevels, true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
index 9dd2dbb..dae69e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
@@ -132,8 +132,7 @@
 
     @VisibleForTesting
     int getDrawableResId() {
-        // TODO(b/357122624): check with UX to obtain the icon for desktop devices.
-        return R.drawable.ic_media_tablet;
+        return R.drawable.ic_media_microphone;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index 0c50166..9164b64 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -25,6 +25,7 @@
 import android.media.AudioManager;
 import android.media.MediaRecorder;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.util.Slog;
 
 import androidx.annotation.NonNull;
@@ -46,6 +47,16 @@
     static final AudioAttributes INPUT_ATTRIBUTES =
             new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.MIC).build();
 
+    @VisibleForTesting
+    static final int[] PRESETS = {
+        MediaRecorder.AudioSource.MIC,
+        MediaRecorder.AudioSource.CAMCORDER,
+        MediaRecorder.AudioSource.VOICE_RECOGNITION,
+        MediaRecorder.AudioSource.VOICE_COMMUNICATION,
+        MediaRecorder.AudioSource.UNPROCESSED,
+        MediaRecorder.AudioSource.VOICE_PERFORMANCE
+    };
+
     private final Context mContext;
 
     private final AudioManager mAudioManager;
@@ -55,6 +66,7 @@
     private MediaDevice mSelectedInputDevice;
 
     private final Collection<InputDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
+    private final Object mCallbackLock = new Object();
 
     @VisibleForTesting
     final AudioDeviceCallback mAudioDeviceCallback =
@@ -76,17 +88,33 @@
         Handler handler = new Handler(context.getMainLooper());
 
         mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, handler);
+
+        mAudioManager.addOnPreferredDevicesForCapturePresetChangedListener(
+                new HandlerExecutor(handler),
+                this::onPreferredDevicesForCapturePresetChangedListener);
     }
 
-    public void registerCallback(@NonNull InputDeviceCallback callback) {
-        if (!mCallbacks.contains(callback)) {
-            mCallbacks.add(callback);
+    private void onPreferredDevicesForCapturePresetChangedListener(
+            @MediaRecorder.SystemSource int capturePreset,
+            @NonNull List<AudioDeviceAttributes> devices) {
+        if (capturePreset == MediaRecorder.AudioSource.MIC) {
             dispatchInputDeviceListUpdate();
         }
     }
 
+    public void registerCallback(@NonNull InputDeviceCallback callback) {
+        synchronized (mCallbackLock) {
+            if (!mCallbacks.contains(callback)) {
+                mCallbacks.add(callback);
+                dispatchInputDeviceListUpdate();
+            }
+        }
+    }
+
     public void unregisterCallback(@NonNull InputDeviceCallback callback) {
-        mCallbacks.remove(callback);
+        synchronized (mCallbackLock) {
+            mCallbacks.remove(callback);
+        }
     }
 
     public @Nullable MediaDevice getSelectedInputDevice() {
@@ -134,8 +162,51 @@
         }
 
         final List<MediaDevice> inputMediaDevices = new ArrayList<>(mInputMediaDevices);
-        for (InputDeviceCallback callback : mCallbacks) {
-            callback.onInputDeviceListUpdated(inputMediaDevices);
+        synchronized (mCallbackLock) {
+            for (InputDeviceCallback callback : mCallbacks) {
+                callback.onInputDeviceListUpdated(inputMediaDevices);
+            }
+        }
+    }
+
+    public void selectDevice(@NonNull MediaDevice device) {
+        if (!(device instanceof InputMediaDevice)) {
+            Slog.w(TAG, "This device is not an InputMediaDevice: " + device.getName());
+            return;
+        }
+
+        if (device.equals(mSelectedInputDevice)) {
+            Slog.w(TAG, "This device is already selected: " + device.getName());
+            return;
+        }
+
+        // Handle edge case where the targeting device is not available, e.g. disconnected.
+        if (!mInputMediaDevices.contains(device)) {
+            Slog.w(TAG, "This device is not available: " + device.getName());
+            return;
+        }
+
+        // TODO(b/355684672): apply address for BT devices.
+        AudioDeviceAttributes deviceAttributes =
+                new AudioDeviceAttributes(
+                        AudioDeviceAttributes.ROLE_INPUT,
+                        ((InputMediaDevice) device).getAudioDeviceInfoType(),
+                        /* address= */ "");
+        try {
+            setPreferredDeviceForAllPresets(deviceAttributes);
+        } catch (IllegalArgumentException e) {
+            Slog.e(
+                    TAG,
+                    "Illegal argument exception while setPreferredDeviceForAllPreset: "
+                            + device.getName(),
+                    e);
+        }
+    }
+
+    private void setPreferredDeviceForAllPresets(@NonNull AudioDeviceAttributes deviceAttributes) {
+        // The input routing via system setting takes effect on all capture presets.
+        for (@MediaRecorder.Source int preset : PRESETS) {
+            mAudioManager.setPreferredDeviceForCapturePreset(preset, deviceAttributes);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 0b8fb22ce..feaf7fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -97,7 +97,7 @@
             case TYPE_USB_ACCESSORY:
                 name =
                         inputRoutingEnabledAndIsDesktop()
-                                ? context.getString(R.string.media_transfer_usb_speaker_name)
+                                ? context.getString(R.string.media_transfer_usb_audio_name)
                                 : context.getString(R.string.media_transfer_wired_headphone_name);
                 break;
             case TYPE_DOCK:
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index c686708..43d7946 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -74,6 +74,10 @@
         mutableModesFlow.value = mutableModesFlow.value.filter { it.id != id }
     }
 
+    fun replaceMode(modeId: String, mode: ZenMode) {
+        mutableModesFlow.value = (mutableModesFlow.value.filter { it.id != modeId }) + mode
+    }
+
     fun getMode(id: String): ZenMode? {
         return mutableModesFlow.value.find { it.id == id }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
index 712ddc8..5eeb49a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
@@ -167,6 +167,13 @@
         return this;
     }
 
+    public TestModeBuilder setVisualEffect(int effect, boolean allowed) {
+        ZenPolicy newPolicy = new ZenPolicy.Builder(mRule.getZenPolicy())
+                .showVisualEffect(effect, allowed).build();
+        setZenPolicy(newPolicy);
+        return this;
+    }
+
     public TestModeBuilder setEnabled(boolean enabled) {
         return setEnabled(enabled, /* byUser= */ false);
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatusTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatusTest.kt
new file mode 100644
index 0000000..3149acf
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigServiceStatusTest.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DeviceSettingsConfigServiceStatusTest {
+
+    @Test
+    fun parcelOperation() {
+        val item =
+            DeviceSettingsConfigServiceStatus(
+                success = true,
+                extras = Bundle().apply { putString("key1", "value1") },
+            )
+
+        val fromParcel = writeAndRead(item)
+
+        assertThat(fromParcel.success).isEqualTo(item.success)
+        assertThat(fromParcel.extras.getString("key1")).isEqualTo(item.extras.getString("key1"))
+    }
+
+    private fun writeAndRead(
+        item: DeviceSettingsConfigServiceStatus
+    ): DeviceSettingsConfigServiceStatus {
+        val parcel = Parcel.obtain()
+        item.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+        return DeviceSettingsConfigServiceStatus.CREATOR.createFromParcel(parcel)
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index 0cb6bc1..4e62fd3b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -33,10 +33,12 @@
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfigServiceStatus
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsProviderServiceStatus
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
+import com.android.settingslib.bluetooth.devicesettings.IGetDeviceSettingsConfigCallback
 import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreference
 import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreferenceState
 import com.android.settingslib.bluetooth.devicesettings.ToggleInfo
@@ -53,7 +55,6 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -140,15 +141,10 @@
             )
     }
 
-    @After
-    fun clean() {
-        DeviceSettingServiceConnection.services.clear()
-    }
-
     @Test
     fun getDeviceSettingsConfig_withMetadata_success() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService1.serviceStatus)
                 .thenReturn(DeviceSettingsProviderServiceStatus(true))
             `when`(settingProviderService2.serviceStatus)
@@ -179,7 +175,7 @@
                     )
                 )
                 .thenReturn("".toByteArray())
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService1.serviceStatus)
                 .thenReturn(DeviceSettingsProviderServiceStatus(true))
             `when`(settingProviderService2.serviceStatus)
@@ -194,7 +190,7 @@
     @Test
     fun getDeviceSettingsConfig_providerServiceNotEnabled_returnNull() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService1.serviceStatus)
                 .thenReturn(DeviceSettingsProviderServiceStatus(false))
             `when`(settingProviderService2.serviceStatus)
@@ -209,7 +205,7 @@
     @Test
     fun getDeviceSettingsConfig_bindingServiceFail_returnNull() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             doReturn(false).`when`(context).bindService(any(), anyInt(), any(), any())
 
             val config = underTest.getDeviceSettingsConfig(cachedDevice)
@@ -221,7 +217,7 @@
     @Test
     fun getDeviceSetting_actionSwitchPreference_success() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
                 input ->
                 input
@@ -247,7 +243,7 @@
     @Test
     fun getDeviceSetting_multiTogglePreference_success() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
                 input ->
                 input
@@ -273,7 +269,7 @@
     @Test
     fun getDeviceSetting_helpPreference_success() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
                 input ->
                 input
@@ -299,6 +295,7 @@
     @Test
     fun getDeviceSetting_noConfig_returnNull() {
         testScope.runTest {
+            setUpConfigService(false, null)
             `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
                 input ->
                 input
@@ -320,7 +317,7 @@
     @Test
     fun updateDeviceSettingState_switchState_success() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
                 input ->
                 input
@@ -358,7 +355,7 @@
     @Test
     fun updateDeviceSettingState_multiToggleState_success() {
         testScope.runTest {
-            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            setUpConfigService(true, DEVICE_SETTING_CONFIG)
             `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
                 input ->
                 input
@@ -459,6 +456,17 @@
         assertThat(actual.settingId).isEqualTo(serviceResponse.settingId)
     }
 
+    private fun setUpConfigService(success: Boolean, config: DeviceSettingsConfig?) {
+        `when`(configService.getDeviceSettingsConfig(any(), any())).then { input ->
+            input
+                .getArgument<IGetDeviceSettingsConfigCallback>(1)
+                .onResult(
+                    DeviceSettingsConfigServiceStatus(success = success),
+                    config
+                )
+        }
+    }
+
     private companion object {
         const val BLUETOOTH_ADDRESS = "12:34:56:78"
         const val CONFIG_SERVICE_PACKAGE_NAME = "com.android.fake.configservice"
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
index bc1ea6c..088d554 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
@@ -18,9 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import android.content.Context;
 import android.media.AudioDeviceInfo;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -64,7 +61,7 @@
                         CURRENT_VOLUME,
                         IS_VOLUME_FIXED);
         assertThat(builtinMediaDevice).isNotNull();
-        assertThat(builtinMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_tablet);
+        assertThat(builtinMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_microphone);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index 8a18d07..16c0c1c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -17,17 +17,21 @@
 package com.android.settingslib.media;
 
 import static com.android.settingslib.media.InputRouteManager.INPUT_ATTRIBUTES;
+import static com.android.settingslib.media.InputRouteManager.PRESETS;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
+import android.media.MediaRecorder;
 
 import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
 
@@ -51,6 +55,9 @@
     private static final int INPUT_USB_DEVICE_ID = 3;
     private static final int INPUT_USB_HEADSET_ID = 4;
     private static final int INPUT_USB_ACCESSORY_ID = 5;
+    private static final int MAX_VOLUME = 1;
+    private static final int CURRENT_VOLUME = 0;
+    private static final boolean VOLUME_FIXED_TRUE = true;
 
     private final Context mContext = spy(RuntimeEnvironment.application);
     private InputRouteManager mInputRouteManager;
@@ -222,6 +229,31 @@
     }
 
     @Test
+    public void selectDevice() {
+        final AudioManager audioManager = mock(AudioManager.class);
+        InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+        final MediaDevice inputMediaDevice =
+                InputMediaDevice.create(
+                        mContext,
+                        String.valueOf(BUILTIN_MIC_ID),
+                        AudioDeviceInfo.TYPE_BUILTIN_MIC,
+                        MAX_VOLUME,
+                        CURRENT_VOLUME,
+                        VOLUME_FIXED_TRUE);
+        inputRouteManager.selectDevice(inputMediaDevice);
+
+        AudioDeviceAttributes deviceAttributes =
+                new AudioDeviceAttributes(
+                        AudioDeviceAttributes.ROLE_INPUT,
+                        AudioDeviceInfo.TYPE_BUILTIN_MIC,
+                        /* address= */ "");
+        for (@MediaRecorder.Source int preset : PRESETS) {
+            verify(audioManager, atLeastOnce())
+                    .setPreferredDeviceForCapturePreset(preset, deviceAttributes);
+        }
+    }
+
+    @Test
     public void getMaxInputGain_returnMaxInputGain() {
         assertThat(mInputRouteManager.getMaxInputGain()).isEqualTo(15);
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index da5f428..1739c0e5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -136,7 +136,7 @@
         when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE);
 
         assertThat(mPhoneMediaDevice.getName())
-                .isEqualTo(mContext.getString(R.string.media_transfer_usb_speaker_name));
+                .isEqualTo(mContext.getString(R.string.media_transfer_usb_audio_name));
 
         when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index d710939..5e31da4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -164,6 +164,7 @@
         Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
         Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
         Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+        Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM,
         Settings.Secure.SHOW_NOTIFICATION_SNOOZE,
         Settings.Secure.NOTIFICATION_HISTORY_ENABLED,
         Settings.Secure.ZEN_DURATION,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index fa16a44..b3f7374 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -243,6 +243,7 @@
         VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SHOW_NOTIFICATION_SNOOZE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.NOTIFICATION_HISTORY_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ZEN_DURATION, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 0ae4da5..749ad0a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -967,7 +967,7 @@
         for (int i = 0; i < nameCount; i++) {
             String name = names.get(i);
             Setting setting = settingsState.getSettingLocked(name);
-            pw.print("_id:"); pw.print(toDumpString(setting.getId()));
+            pw.print("_id:"); pw.print(toDumpString(String.valueOf(setting.getId())));
             pw.print(" name:"); pw.print(toDumpString(name));
             if (setting.getPackageName() != null) {
                 pw.print(" pkg:"); pw.print(setting.getPackageName());
@@ -2016,8 +2016,6 @@
             if (!isValidMediaUri(name, value)) {
                 return false;
             }
-            // Invalidate any relevant cache files
-            cacheFile.delete();
         }
 
         final boolean success;
@@ -2055,6 +2053,11 @@
             return false;
         }
 
+        if (cacheFile != null) {
+            // Invalidate any relevant cache files
+            cacheFile.delete();
+        }
+
         if ((operation == MUTATION_OPERATION_INSERT || operation == MUTATION_OPERATION_UPDATE)
                 && cacheFile != null && value != null) {
             final Uri ringtoneUri = Uri.parse(value);
@@ -2782,7 +2785,7 @@
 
             switch (column) {
                 case Settings.NameValueTable._ID -> {
-                    values[i] = setting.getId();
+                    values[i] = String.valueOf(setting.getId());
                 }
                 case Settings.NameValueTable.NAME -> {
                     values[i] = setting.getName();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 452edd9..3c634f0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -517,6 +517,7 @@
         }
 
         String namespace = name.substring(0, slashIdx);
+        namespace = namespace.intern();  // Many configs have the same namespace.
         String fullFlagName = name.substring(slashIdx + 1);
         boolean isLocal = false;
 
@@ -566,7 +567,7 @@
         }
         try {
             localCounter = Integer.parseInt(markerSetting.value);
-        } catch(NumberFormatException e) {
+        } catch (NumberFormatException e) {
             // reset local counter
             markerSetting.value = "0";
         }
@@ -1363,7 +1364,10 @@
                     }
 
                     try {
-                        if (writeSingleSetting(mVersion, serializer, setting.getId(),
+                        if (writeSingleSetting(
+                                mVersion,
+                                serializer,
+                                Long.toString(setting.getId()),
                                 setting.getName(),
                                 setting.getValue(), setting.getDefaultValue(),
                                 setting.getPackageName(),
@@ -1632,7 +1636,7 @@
             TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parseStateLocked(parser);
             return true;
-        } catch (XmlPullParserException | IOException e) {
+        } catch (XmlPullParserException | IOException | NumberFormatException e) {
             Slog.e(LOG_TAG, "parse settings xml failed", e);
             return false;
         } finally {
@@ -1652,7 +1656,7 @@
     }
 
     private void parseStateLocked(TypedXmlPullParser parser)
-            throws IOException, XmlPullParserException {
+            throws IOException, XmlPullParserException, NumberFormatException {
         final int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1708,7 +1712,7 @@
 
     @GuardedBy("mLock")
     private void parseSettingsLocked(TypedXmlPullParser parser)
-            throws IOException, XmlPullParserException {
+            throws IOException, XmlPullParserException, NumberFormatException {
 
         mVersion = parser.getAttributeInt(null, ATTR_VERSION);
 
@@ -1776,7 +1780,7 @@
                     }
                 }
                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
-                        fromSystem, id, isPreservedInRestore));
+                        fromSystem, Long.valueOf(id), isPreservedInRestore));
 
                 if (DEBUG_PERSISTENCE) {
                     Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
@@ -1866,7 +1870,7 @@
         private String value;
         private String defaultValue;
         private String packageName;
-        private String id;
+        private long id;
         private String tag;
         // Whether the default is set by the system
         private boolean defaultFromSystem;
@@ -1898,30 +1902,27 @@
         }
 
         public Setting(String name, String value, String defaultValue,
-                String packageName, String tag, boolean fromSystem, String id) {
+                String packageName, String tag, boolean fromSystem, long id) {
             this(name, value, defaultValue, packageName, tag, fromSystem, id,
                     /* isOverrideableByRestore */ false);
         }
 
         Setting(String name, String value, String defaultValue,
-                String packageName, String tag, boolean fromSystem, String id,
+                String packageName, String tag, boolean fromSystem, long id,
                 boolean isValuePreservedInRestore) {
-            mNextId = Math.max(mNextId, Long.parseLong(id) + 1);
-            if (NULL_VALUE.equals(value)) {
-                value = null;
-            }
+            mNextId = Math.max(mNextId, id + 1);
             init(name, value, tag, defaultValue, packageName, fromSystem, id,
                     isValuePreservedInRestore);
         }
 
         private void init(String name, String value, String tag, String defaultValue,
-                String packageName, boolean fromSystem, String id,
+                String packageName, boolean fromSystem, long id,
                 boolean isValuePreservedInRestore) {
             this.name = name;
-            this.value = value;
+            this.value = internValue(value);
             this.tag = tag;
-            this.defaultValue = defaultValue;
-            this.packageName = packageName;
+            this.defaultValue = internValue(defaultValue);
+            this.packageName = TextUtils.safeIntern(packageName);
             this.id = id;
             this.defaultFromSystem = fromSystem;
             this.isValuePreservedInRestore = isValuePreservedInRestore;
@@ -1959,7 +1960,7 @@
             return isValuePreservedInRestore;
         }
 
-        public String getId() {
+        public long getId() {
             return id;
         }
 
@@ -1992,9 +1993,6 @@
         private boolean update(String value, boolean setDefault, String packageName, String tag,
                 boolean forceNonSystemPackage, boolean overrideableByRestore,
                 boolean resetToDefault) {
-            if (NULL_VALUE.equals(value)) {
-                value = null;
-            }
             final boolean callerSystem = !forceNonSystemPackage &&
                     !isNull() && (isCalledFromSystem(packageName)
                     || isSystemPackage(mContext, packageName));
@@ -2039,7 +2037,7 @@
             }
 
             init(name, value, tag, defaultValue, packageName, defaultFromSystem,
-                    String.valueOf(mNextId++), isPreserved);
+                    mNextId++, isPreserved);
 
             return true;
         }
@@ -2051,6 +2049,32 @@
                     + " defaultFromSystem=" + defaultFromSystem + "}";
         }
 
+        /**
+         * Interns a string if it's a common setting value.
+         * Otherwise returns the given string.
+         */
+        static String internValue(String str) {
+            if (str == null) {
+                return null;
+            }
+            switch (str) {
+                case "true":
+                    return "true";
+                case "false":
+                    return "false";
+                case "0":
+                    return "0";
+                case "1":
+                    return "1";
+                case "":
+                    return "";
+                case "null":
+                    return null;  // explicit null has special handling
+                default:
+                    return str;
+            }
+        }
+
         private boolean shouldPreserveSetting(boolean overrideableByRestore,
                 boolean resetToDefault, String packageName, String value) {
             if (resetToDefault) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index cd16af7..bd7067b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -860,6 +860,7 @@
     resource_dirs: [],
     kotlincflags: ["-Xjvm-default=all"],
     optimize: {
+        optimize: false,
         shrink_resources: false,
         optimized_shrink_resources: false,
         proguard_flags_files: ["proguard.flags"],
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 157af7d..9726ecf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -1013,6 +1013,7 @@
             android:excludeFromRecents="true"
             android:autoRemoveFromRecents="true"
             android:launchMode="singleTop"
+            android:showForAllUsers="true"
             android:exported="false">
         </activity>
 
diff --git a/packages/SystemUI/TEST_OWNERS b/packages/SystemUI/TEST_OWNERS
new file mode 100644
index 0000000..eadc86e
--- /dev/null
+++ b/packages/SystemUI/TEST_OWNERS
@@ -0,0 +1,5 @@
+# Test maintainers for system UI
+# usernames listed here are able to approve changes to sysui unit tests,
+# for restructuring and test maintenance only
+
+saff@google.com
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c49ffb4..88cc152 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -40,16 +40,6 @@
 }
 
 flag {
-   name: "notification_minimalism_prototype"
-   namespace: "systemui"
-   description: "Prototype of notification minimalism; the new 'Intermediate' lockscreen customization proposal."
-   bug: "330387368"
-   metadata {
-        purpose: PURPOSE_BUGFIX
-   }
-}
-
-flag {
    name: "notification_view_flipper_pausing_v2"
    namespace: "systemui"
    description: "Pause ViewFlippers inside Notification custom layouts when the shade is closed."
@@ -149,16 +139,6 @@
 }
 
 flag {
-   name: "modes_dialog_single_rows"
-   namespace: "systemui"
-   description: "[Experiment] Display one entry per grid row in the Modes Dialog."
-   bug: "366034002"
-   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"
@@ -599,6 +579,16 @@
 }
 
 flag {
+    name: "clipboard_use_description_mimetype"
+    namespace: "systemui"
+    description: "Read item mimetype from description rather than checking URI"
+    bug: "357197236"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "screenshot_action_dismiss_system_windows"
     namespace: "systemui"
     description: "Dismiss existing system windows when starting action from screenshot UI"
diff --git a/packages/SystemUI/animation/lib/Android.bp b/packages/SystemUI/animation/lib/Android.bp
index 4324d463..d9230ec 100644
--- a/packages/SystemUI/animation/lib/Android.bp
+++ b/packages/SystemUI/animation/lib/Android.bp
@@ -33,6 +33,20 @@
     ],
 }
 
+// This is the core animation library written in java and can be depended by java sdk libraries.
+// Please don't introduce kotlin code in this target since kotlin is incompatible with sdk
+// libraries.
+java_library {
+    name: "PlatformAnimationLib-core",
+    srcs: [
+        "src/com/android/systemui/animation/*.java",
+        ":PlatformAnimationLib-aidl",
+    ],
+    static_libs: [
+        "WindowManager-Shell-shared",
+    ],
+}
+
 filegroup {
     name: "PlatformAnimationLib-aidl",
     srcs: [
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
new file mode 100644
index 0000000..08db95e
--- /dev/null
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -0,0 +1,374 @@
+/*
+ * 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.animation;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.TransitionInfo;
+import android.window.TransitionInfo.Change;
+import android.window.WindowAnimationState;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.wm.shell.shared.TransitionUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An implementation of {@link IRemoteTransition} that accepts a {@link UIComponent} as the origin
+ * and automatically attaches it to the transition leash before the transition starts.
+ */
+public class OriginRemoteTransition extends IRemoteTransition.Stub {
+    private static final String TAG = "OriginRemoteTransition";
+
+    private final Context mContext;
+    private final boolean mIsEntry;
+    private final UIComponent mOrigin;
+    private final TransitionPlayer mPlayer;
+    private final long mDuration;
+    private final Handler mHandler;
+
+    @Nullable private SurfaceControl.Transaction mStartTransaction;
+    @Nullable private IRemoteTransitionFinishedCallback mFinishCallback;
+    @Nullable private UIComponent.Transaction mOriginTransaction;
+    @Nullable private ValueAnimator mAnimator;
+    @Nullable private SurfaceControl mOriginLeash;
+    private boolean mCancelled;
+
+    OriginRemoteTransition(
+            Context context,
+            boolean isEntry,
+            UIComponent origin,
+            TransitionPlayer player,
+            long duration,
+            Handler handler) {
+        mContext = context;
+        mIsEntry = isEntry;
+        mOrigin = origin;
+        mPlayer = player;
+        mDuration = duration;
+        mHandler = handler;
+    }
+
+    @Override
+    public void startAnimation(
+            IBinder token,
+            TransitionInfo info,
+            SurfaceControl.Transaction t,
+            IRemoteTransitionFinishedCallback finishCallback) {
+        logD("startAnimation - " + info);
+        mHandler.post(
+                () -> {
+                    mStartTransaction = t;
+                    mFinishCallback = finishCallback;
+                    startAnimationInternal(info);
+                });
+    }
+
+    @Override
+    public void mergeAnimation(
+            IBinder transition,
+            TransitionInfo info,
+            SurfaceControl.Transaction t,
+            IBinder mergeTarget,
+            IRemoteTransitionFinishedCallback finishCallback) {
+        logD("mergeAnimation - " + info);
+        mHandler.post(this::cancel);
+    }
+
+    @Override
+    public void takeOverAnimation(
+            IBinder transition,
+            TransitionInfo info,
+            SurfaceControl.Transaction t,
+            IRemoteTransitionFinishedCallback finishCallback,
+            WindowAnimationState[] states) {
+        logD("takeOverAnimation - " + info);
+    }
+
+    @Override
+    public void onTransitionConsumed(IBinder transition, boolean aborted) {
+        logD("onTransitionConsumed - aborted: " + aborted);
+        mHandler.post(this::cancel);
+    }
+
+    private void startAnimationInternal(TransitionInfo info) {
+        if (!prepareUIs(info)) {
+            logE("Unable to prepare UI!");
+            finishAnimation(/* finished= */ false);
+            return;
+        }
+        // Notify player that we are starting.
+        mPlayer.onStart(info, mStartTransaction, mOrigin, mOriginTransaction);
+
+        // Start the animator.
+        mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+        mAnimator.setDuration(mDuration);
+        mAnimator.addListener(
+                new AnimatorListener() {
+                    @Override
+                    public void onAnimationStart(Animator a) {}
+
+                    @Override
+                    public void onAnimationEnd(Animator a) {
+                        finishAnimation(/* finished= */ !mCancelled);
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator a) {
+                        mCancelled = true;
+                    }
+
+                    @Override
+                    public void onAnimationRepeat(Animator a) {}
+                });
+        mAnimator.addUpdateListener(
+                a -> {
+                    mPlayer.onProgress((float) a.getAnimatedValue());
+                });
+        mAnimator.start();
+    }
+
+    private boolean prepareUIs(TransitionInfo info) {
+        if (info.getRootCount() == 0) {
+            logE("prepareUIs: no root leash!");
+            return false;
+        }
+        if (info.getRootCount() > 1) {
+            logE("prepareUIs: multi-display transition is not supported yet!");
+            return false;
+        }
+        if (info.getChanges().isEmpty()) {
+            logE("prepareUIs: no changes!");
+            return false;
+        }
+
+        SurfaceControl rootLeash = info.getRoot(0).getLeash();
+        int displayId = info.getChanges().get(0).getEndDisplayId();
+        Rect displayBounds = getDisplayBounds(displayId);
+        float windowRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
+        logD("prepareUIs: windowRadius=" + windowRadius + ", displayBounds=" + displayBounds);
+
+        // Create the origin leash and add to the transition root leash.
+        mOriginLeash =
+                new SurfaceControl.Builder().setName("OriginTransition-origin-leash").build();
+        mStartTransaction
+                .reparent(mOriginLeash, rootLeash)
+                .show(mOriginLeash)
+                .setCornerRadius(mOriginLeash, windowRadius)
+                .setWindowCrop(mOriginLeash, displayBounds.width(), displayBounds.height());
+
+        // Process surfaces
+        List<SurfaceControl> openingSurfaces = new ArrayList<>();
+        List<SurfaceControl> closingSurfaces = new ArrayList<>();
+        for (Change change : info.getChanges()) {
+            int mode = change.getMode();
+            SurfaceControl leash = change.getLeash();
+            // Reparent leash to the transition root.
+            mStartTransaction.reparent(leash, rootLeash);
+            if (TransitionUtil.isOpeningMode(mode)) {
+                openingSurfaces.add(change.getLeash());
+                // For opening surfaces, ending bounds are base bound. Apply corner radius if
+                // it's full screen.
+                Rect bounds = change.getEndAbsBounds();
+                if (displayBounds.equals(bounds)) {
+                    mStartTransaction
+                            .setCornerRadius(leash, windowRadius)
+                            .setWindowCrop(leash, bounds.width(), bounds.height());
+                }
+            } else if (TransitionUtil.isClosingMode(mode)) {
+                closingSurfaces.add(change.getLeash());
+                // For closing surfaces, starting bounds are base bounds. Apply corner radius if
+                // it's full screen.
+                Rect bounds = change.getStartAbsBounds();
+                if (displayBounds.equals(bounds)) {
+                    mStartTransaction
+                            .setCornerRadius(leash, windowRadius)
+                            .setWindowCrop(leash, bounds.width(), bounds.height());
+                }
+            }
+        }
+
+        // Set relative order:
+        // ----  App1  ----
+        // ---- origin ----
+        // ----  App2  ----
+        if (mIsEntry) {
+            mStartTransaction
+                    .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1)
+                    .setRelativeLayer(
+                            openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+        } else {
+            mStartTransaction
+                    .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1)
+                    .setRelativeLayer(
+                            closingSurfaces.get(closingSurfaces.size() - 1), mOriginLeash, 1);
+        }
+
+        // Attach origin UIComponent to origin leash.
+        mOriginTransaction = mOrigin.newTransaction();
+        mOriginTransaction
+                .attachToTransitionLeash(
+                        mOrigin, mOriginLeash, displayBounds.width(), displayBounds.height())
+                .commit();
+
+        // Apply all surface changes.
+        mStartTransaction.apply();
+        return true;
+    }
+
+    private Rect getDisplayBounds(int displayId) {
+        DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+        DisplayMetrics metrics = new DisplayMetrics();
+        dm.getDisplay(displayId).getMetrics(metrics);
+        return new Rect(0, 0, metrics.widthPixels, metrics.heightPixels);
+    }
+
+    private void finishAnimation(boolean finished) {
+        logD("finishAnimation: finished=" + finished);
+        if (mAnimator == null) {
+            // The transition didn't start. Ensure we apply the start transaction and report
+            // finish afterwards.
+            mStartTransaction
+                    .addTransactionCommittedListener(
+                            mContext.getMainExecutor(), this::finishInternal)
+                    .apply();
+            return;
+        }
+        mAnimator = null;
+        // Notify client that we have ended.
+        mPlayer.onEnd(finished);
+        // Detach the origin from the transition leash and report finish after it's done.
+        mOriginTransaction
+                .detachFromTransitionLeash(
+                        mOrigin, mContext.getMainExecutor(), this::finishInternal)
+                .commit();
+    }
+
+    private void finishInternal() {
+        logD("finishInternal");
+        if (mOriginLeash != null) {
+            // Release origin leash.
+            mOriginLeash.release();
+            mOriginLeash = null;
+        }
+        try {
+            mFinishCallback.onTransitionFinished(null, null);
+        } catch (RemoteException e) {
+            logE("Unable to report transition finish!", e);
+        }
+        mStartTransaction = null;
+        mOriginTransaction = null;
+        mFinishCallback = null;
+    }
+
+    private void cancel() {
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+    }
+
+    private static void logD(String msg) {
+        if (OriginTransitionSession.DEBUG) {
+            Log.d(TAG, msg);
+        }
+    }
+
+    private static void logE(String msg) {
+        Log.e(TAG, msg);
+    }
+
+    private static void logE(String msg, Throwable e) {
+        Log.e(TAG, msg, e);
+    }
+
+    private static UIComponent wrapSurfaces(TransitionInfo info, boolean isOpening) {
+        List<SurfaceControl> surfaces = new ArrayList<>();
+        Rect maxBounds = new Rect();
+        for (Change change : info.getChanges()) {
+            int mode = change.getMode();
+            if (TransitionUtil.isOpeningMode(mode) == isOpening) {
+                surfaces.add(change.getLeash());
+                Rect bounds = isOpening ? change.getEndAbsBounds() : change.getStartAbsBounds();
+                maxBounds.union(bounds);
+            }
+        }
+        return new SurfaceUIComponent(
+                surfaces,
+                /* alpha= */ 1.0f,
+                /* visible= */ true,
+                /* bounds= */ maxBounds,
+                /* baseBounds= */ maxBounds);
+    }
+
+    /** An interface that represents an origin transitions. */
+    public interface TransitionPlayer {
+
+        /**
+         * Called when an origin transition starts. This method exposes the raw {@link
+         * TransitionInfo} so that clients can extract more information from it.
+         */
+        default void onStart(
+                TransitionInfo transitionInfo,
+                SurfaceControl.Transaction sfTransaction,
+                UIComponent origin,
+                UIComponent.Transaction uiTransaction) {
+            // Wrap transactions.
+            Transactions transactions =
+                    new Transactions()
+                            .registerTransactionForClass(origin.getClass(), uiTransaction)
+                            .registerTransactionForClass(
+                                    SurfaceUIComponent.class,
+                                    new SurfaceUIComponent.Transaction(sfTransaction));
+            // Wrap surfaces and start.
+            onStart(
+                    transactions,
+                    origin,
+                    wrapSurfaces(transitionInfo, /* isOpening= */ false),
+                    wrapSurfaces(transitionInfo, /* isOpening= */ true));
+        }
+
+        /**
+         * Called when an origin transition starts. This method exposes the opening and closing
+         * windows as wrapped {@link UIComponent} to provide simplified interface to clients.
+         */
+        void onStart(
+                UIComponent.Transaction transaction,
+                UIComponent origin,
+                UIComponent closingApp,
+                UIComponent openingApp);
+
+        /** Called to update the transition frame. */
+        void onProgress(float progress);
+
+        /** Called when the transition ended. */
+        void onEnd(boolean finished);
+    }
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
new file mode 100644
index 0000000..23693b6
--- /dev/null
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
@@ -0,0 +1,321 @@
+/*
+ * 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.animation;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.ActivityOptions.LaunchCookie;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.window.IRemoteTransition;
+import android.window.RemoteTransition;
+
+import com.android.systemui.animation.OriginRemoteTransition.TransitionPlayer;
+import com.android.systemui.animation.shared.IOriginTransitions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * A session object that holds origin transition states for starting an activity from an on-screen
+ * UI component and smoothly transitioning back from the activity to the same UI component.
+ */
+public class OriginTransitionSession {
+    private static final String TAG = "OriginTransitionSession";
+    static final boolean DEBUG = Build.IS_USERDEBUG || Log.isLoggable(TAG, Log.DEBUG);
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {NOT_STARTED, STARTED, CANCELLED})
+    private @interface State {}
+
+    @State private static final int NOT_STARTED = 0;
+    @State private static final int STARTED = 1;
+    @State private static final int CANCELLED = 5;
+
+    private final String mName;
+    @Nullable private final IOriginTransitions mOriginTransitions;
+    private final Predicate<RemoteTransition> mIntentStarter;
+    @Nullable private final IRemoteTransition mEntryTransition;
+    @Nullable private final IRemoteTransition mExitTransition;
+    private final AtomicInteger mState = new AtomicInteger(NOT_STARTED);
+
+    @Nullable private RemoteTransition mOriginTransition;
+
+    private OriginTransitionSession(
+            String name,
+            @Nullable IOriginTransitions originTransitions,
+            Predicate<RemoteTransition> intentStarter,
+            @Nullable IRemoteTransition entryTransition,
+            @Nullable IRemoteTransition exitTransition) {
+        mName = name;
+        mOriginTransitions = originTransitions;
+        mIntentStarter = intentStarter;
+        mEntryTransition = entryTransition;
+        mExitTransition = exitTransition;
+        if (hasExitTransition() && !hasEntryTransition()) {
+            throw new IllegalArgumentException(
+                    "Entry transition must be supplied if you want to play an exit transition!");
+        }
+    }
+
+    /**
+     * Launch the target intent with the supplied entry transition. After this method, the entry
+     * transition is expected to receive callbacks. The exit transition will be registered and
+     * triggered when the system detects a return from the launched activity to the launching
+     * activity.
+     */
+    public boolean start() {
+        if (!mState.compareAndSet(NOT_STARTED, STARTED)) {
+            logE("start: illegal state - " + stateToString(mState.get()));
+            return false;
+        }
+
+        RemoteTransition remoteTransition = null;
+        if (hasEntryTransition() && hasExitTransition()) {
+            logD("start: starting with entry and exit transition.");
+            try {
+                remoteTransition =
+                        mOriginTransition =
+                                mOriginTransitions.makeOriginTransition(
+                                        new RemoteTransition(mEntryTransition, mName + "-entry"),
+                                        new RemoteTransition(mExitTransition, mName + "-exit"));
+            } catch (RemoteException e) {
+                logE("Unable to create origin transition!", e);
+            }
+        } else if (hasEntryTransition()) {
+            logD("start: starting with entry transition.");
+            remoteTransition = new RemoteTransition(mEntryTransition, mName + "-entry");
+
+        } else {
+            logD("start: starting without transition.");
+        }
+        if (mIntentStarter.test(remoteTransition)) {
+            return true;
+        } else {
+            // Animation is cancelled by intent starter.
+            logD("start: cancelled by intent starter!");
+            cancel();
+            return false;
+        }
+    }
+
+    /**
+     * Cancel the current transition and the registered exit transition if it exists. After this
+     * method, this session object can no longer be used. Clients need to create a new session
+     * object if they want to launch another intent with origin transition.
+     */
+    public void cancel() {
+        final int lastState = mState.getAndSet(CANCELLED);
+        if (lastState == CANCELLED || lastState == NOT_STARTED) {
+            return;
+        }
+        logD("cancel: cancelled transition. Last state: " + stateToString(lastState));
+        if (mOriginTransition != null) {
+            try {
+                mOriginTransitions.cancelOriginTransition(mOriginTransition);
+                mOriginTransition = null;
+            } catch (RemoteException e) {
+                logE("Unable to cancel origin transition!", e);
+            }
+        }
+    }
+
+    private boolean hasEntryTransition() {
+        return mEntryTransition != null;
+    }
+
+    private boolean hasExitTransition() {
+        return mOriginTransitions != null && mExitTransition != null;
+    }
+
+    private void logD(String msg) {
+        if (DEBUG) {
+            Log.d(TAG, "Session[" + mName + "] - " + msg);
+        }
+    }
+
+    private void logE(String msg) {
+        Log.e(TAG, "Session[" + mName + "] - " + msg);
+    }
+
+    private void logE(String msg, Throwable e) {
+        Log.e(TAG, "Session[" + mName + "] - " + msg, e);
+    }
+
+    private static String stateToString(@State int state) {
+        switch (state) {
+            case NOT_STARTED:
+                return "NOT_STARTED";
+            case STARTED:
+                return "STARTED";
+            case CANCELLED:
+                return "CANCELLED";
+            default:
+                return "UNKNOWN(" + state + ")";
+        }
+    }
+
+    /** A builder to build a {@link OriginTransitionSession}. */
+    public static class Builder {
+        private final Context mContext;
+        @Nullable private final IOriginTransitions mOriginTransitions;
+        @Nullable private Supplier<IRemoteTransition> mEntryTransitionSupplier;
+        @Nullable private Supplier<IRemoteTransition> mExitTransitionSupplier;
+        private Handler mHandler = new Handler(Looper.getMainLooper());
+        private String mName;
+        @Nullable private Predicate<RemoteTransition> mIntentStarter;
+
+        /** Create a builder that only supports entry transition. */
+        public Builder(Context context) {
+            this(context, /* originTransitions= */ null);
+        }
+
+        /** Create a builder that supports both entry and return transition. */
+        public Builder(Context context, @Nullable IOriginTransitions originTransitions) {
+            mContext = context;
+            mOriginTransitions = originTransitions;
+            mName = context.getPackageName();
+        }
+
+        /** Specify a name that is used in logging. */
+        public Builder withName(String name) {
+            mName = name;
+            return this;
+        }
+
+        /** Specify an intent that will be launched when the session started. */
+        public Builder withIntent(Intent intent) {
+            return withIntentStarter(
+                    transition -> {
+                        mContext.startActivity(
+                                intent, createDefaultActivityOptions(transition).toBundle());
+                        return true;
+                    });
+        }
+
+        /** Specify a pending intent that will be launched when the session started. */
+        public Builder withPendingIntent(PendingIntent pendingIntent) {
+            return withIntentStarter(
+                    transition -> {
+                        try {
+                            pendingIntent.send(createDefaultActivityOptions(transition).toBundle());
+                            return true;
+                        } catch (PendingIntent.CanceledException e) {
+                            Log.e(TAG, "Failed to launch pending intent!", e);
+                            return false;
+                        }
+                    });
+        }
+
+        private static ActivityOptions createDefaultActivityOptions(
+                @Nullable RemoteTransition transition) {
+            ActivityOptions options =
+                    transition == null
+                            ? ActivityOptions.makeBasic()
+                            : ActivityOptions.makeRemoteTransition(transition);
+            LaunchCookie cookie = new LaunchCookie();
+            options.setLaunchCookie(cookie);
+            return options;
+        }
+
+        /**
+         * Specify an intent starter function that will be called to start an activity. The function
+         * accepts an optional {@link RemoteTransition} object which can be used to create an {@link
+         * ActivityOptions} for the activity launch. The function can also return a {@code false}
+         * result to cancel the session.
+         *
+         * <p>Note: it's encouraged to use {@link #withIntent(Intent)} or {@link
+         * #withPendingIntent(PendingIntent)} instead of this method. Use it only if the default
+         * activity launch code doesn't satisfy your requirement.
+         */
+        public Builder withIntentStarter(Predicate<RemoteTransition> intentStarter) {
+            mIntentStarter = intentStarter;
+            return this;
+        }
+
+        /** Add an entry transition to the builder. */
+        public Builder withEntryTransition(IRemoteTransition transition) {
+            mEntryTransitionSupplier = () -> transition;
+            return this;
+        }
+
+        /** Add an origin entry transition to the builder. */
+        public Builder withEntryTransition(
+                UIComponent entryOrigin, TransitionPlayer entryPlayer, long entryDuration) {
+            mEntryTransitionSupplier =
+                    () ->
+                            new OriginRemoteTransition(
+                                    mContext,
+                                    /* isEntry= */ true,
+                                    entryOrigin,
+                                    entryPlayer,
+                                    entryDuration,
+                                    mHandler);
+            return this;
+        }
+
+        /** Add an exit transition to the builder. */
+        public Builder withExitTransition(IRemoteTransition transition) {
+            mExitTransitionSupplier = () -> transition;
+            return this;
+        }
+
+        /** Add an origin exit transition to the builder. */
+        public Builder withExitTransition(
+                UIComponent exitTarget, TransitionPlayer exitPlayer, long exitDuration) {
+            mExitTransitionSupplier =
+                    () ->
+                            new OriginRemoteTransition(
+                                    mContext,
+                                    /* isEntry= */ false,
+                                    exitTarget,
+                                    exitPlayer,
+                                    exitDuration,
+                                    mHandler);
+            return this;
+        }
+
+        /** Supply a handler where transition callbacks will run. */
+        public Builder withHandler(Handler handler) {
+            mHandler = handler;
+            return this;
+        }
+
+        /** Build an {@link OriginTransitionSession}. */
+        public OriginTransitionSession build() {
+            if (mIntentStarter == null) {
+                throw new IllegalArgumentException("No intent, pending intent, or intent starter!");
+            }
+            return new OriginTransitionSession(
+                    mName,
+                    mOriginTransitions,
+                    mIntentStarter,
+                    mEntryTransitionSupplier == null ? null : mEntryTransitionSupplier.get(),
+                    mExitTransitionSupplier == null ? null : mExitTransitionSupplier.get());
+        }
+    }
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
new file mode 100644
index 0000000..2438736
--- /dev/null
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
@@ -0,0 +1,169 @@
+/*
+ * 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.animation;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.SurfaceControl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.Executor;
+
+/** A {@link UIComponent} representing a {@link SurfaceControl}. */
+public class SurfaceUIComponent implements UIComponent {
+    private final Collection<SurfaceControl> mSurfaces;
+    private final Rect mBaseBounds;
+    private final float[] mFloat9 = new float[9];
+
+    private float mAlpha;
+    private boolean mVisible;
+    private Rect mBounds;
+
+    public SurfaceUIComponent(
+            SurfaceControl sc, float alpha, boolean visible, Rect bounds, Rect baseBounds) {
+        this(Arrays.asList(sc), alpha, visible, bounds, baseBounds);
+    }
+
+    public SurfaceUIComponent(
+            Collection<SurfaceControl> surfaces,
+            float alpha,
+            boolean visible,
+            Rect bounds,
+            Rect baseBounds) {
+        mSurfaces = surfaces;
+        mAlpha = alpha;
+        mVisible = visible;
+        mBounds = bounds;
+        mBaseBounds = baseBounds;
+    }
+
+    @Override
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    @Override
+    public boolean isVisible() {
+        return mVisible;
+    }
+
+    @Override
+    public Rect getBounds() {
+        return mBounds;
+    }
+
+    @Override
+    public Transaction newTransaction() {
+        return new Transaction(new SurfaceControl.Transaction());
+    }
+
+    @Override
+    public String toString() {
+        return "SurfaceUIComponent{mSurfaces="
+                + mSurfaces
+                + ", mAlpha="
+                + mAlpha
+                + ", mVisible="
+                + mVisible
+                + ", mBounds="
+                + mBounds
+                + ", mBaseBounds="
+                + mBaseBounds
+                + "}";
+    }
+
+    /** A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}. */
+    public static class Transaction implements UIComponent.Transaction<SurfaceUIComponent> {
+        private final SurfaceControl.Transaction mTransaction;
+        private final ArrayList<Runnable> mChanges = new ArrayList<>();
+
+        public Transaction(SurfaceControl.Transaction transaction) {
+            mTransaction = transaction;
+        }
+
+        @Override
+        public Transaction setAlpha(SurfaceUIComponent ui, float alpha) {
+            mChanges.add(
+                    () -> {
+                        ui.mAlpha = alpha;
+                        ui.mSurfaces.forEach(s -> mTransaction.setAlpha(s, alpha));
+                    });
+            return this;
+        }
+
+        @Override
+        public Transaction setVisible(SurfaceUIComponent ui, boolean visible) {
+            mChanges.add(
+                    () -> {
+                        ui.mVisible = visible;
+                        if (visible) {
+                            ui.mSurfaces.forEach(s -> mTransaction.show(s));
+                        } else {
+                            ui.mSurfaces.forEach(s -> mTransaction.hide(s));
+                        }
+                    });
+            return this;
+        }
+
+        @Override
+        public Transaction setBounds(SurfaceUIComponent ui, Rect bounds) {
+            mChanges.add(
+                    () -> {
+                        if (ui.mBounds.equals(bounds)) {
+                            return;
+                        }
+                        ui.mBounds = bounds;
+                        Matrix matrix = new Matrix();
+                        matrix.setRectToRect(
+                                new RectF(ui.mBaseBounds),
+                                new RectF(ui.mBounds),
+                                Matrix.ScaleToFit.FILL);
+                        ui.mSurfaces.forEach(s -> mTransaction.setMatrix(s, matrix, ui.mFloat9));
+                    });
+            return this;
+        }
+
+        @Override
+        public Transaction attachToTransitionLeash(
+                SurfaceUIComponent ui, SurfaceControl transitionLeash, int w, int h) {
+            mChanges.add(
+                    () -> ui.mSurfaces.forEach(s -> mTransaction.reparent(s, transitionLeash)));
+            return this;
+        }
+
+        @Override
+        public Transaction detachFromTransitionLeash(
+                SurfaceUIComponent ui, Executor executor, Runnable onDone) {
+            mChanges.add(
+                    () -> {
+                        ui.mSurfaces.forEach(s -> mTransaction.reparent(s, null));
+                        mTransaction.addTransactionCommittedListener(executor, onDone::run);
+                    });
+            return this;
+        }
+
+        @Override
+        public void commit() {
+            mChanges.forEach(Runnable::run);
+            mChanges.clear();
+            mTransaction.apply();
+        }
+    }
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
new file mode 100644
index 0000000..5240d99
--- /dev/null
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
@@ -0,0 +1,86 @@
+/*
+ * 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.animation;
+
+import android.annotation.FloatRange;
+import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * A composite {@link UIComponent.Transaction} that combines multiple other transactions for each ui
+ * type.
+ */
+public class Transactions implements UIComponent.Transaction<UIComponent> {
+    private final Map<Class, UIComponent.Transaction> mTransactions = new ArrayMap<>();
+
+    /** Register a transaction object for updating a certain {@link UIComponent} type. */
+    public <T extends UIComponent> Transactions registerTransactionForClass(
+            Class<T> clazz, UIComponent.Transaction transaction) {
+        mTransactions.put(clazz, transaction);
+        return this;
+    }
+
+    private UIComponent.Transaction getTransactionFor(UIComponent ui) {
+        UIComponent.Transaction transaction = mTransactions.get(ui.getClass());
+        if (transaction == null) {
+            transaction = ui.newTransaction();
+            mTransactions.put(ui.getClass(), transaction);
+        }
+        return transaction;
+    }
+
+    @Override
+    public Transactions setAlpha(UIComponent ui, @FloatRange(from = 0.0, to = 1.0) float alpha) {
+        getTransactionFor(ui).setAlpha(ui, alpha);
+        return this;
+    }
+
+    @Override
+    public Transactions setVisible(UIComponent ui, boolean visible) {
+        getTransactionFor(ui).setVisible(ui, visible);
+        return this;
+    }
+
+    @Override
+    public Transactions setBounds(UIComponent ui, Rect bounds) {
+        getTransactionFor(ui).setBounds(ui, bounds);
+        return this;
+    }
+
+    @Override
+    public Transactions attachToTransitionLeash(
+            UIComponent ui, SurfaceControl transitionLeash, int w, int h) {
+        getTransactionFor(ui).attachToTransitionLeash(ui, transitionLeash, w, h);
+        return this;
+    }
+
+    @Override
+    public Transactions detachFromTransitionLeash(
+            UIComponent ui, Executor executor, Runnable onDone) {
+        getTransactionFor(ui).detachFromTransitionLeash(ui, executor, onDone);
+        return this;
+    }
+
+    @Override
+    public void commit() {
+        mTransactions.values().forEach(UIComponent.Transaction::commit);
+    }
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
new file mode 100644
index 0000000..747e4d1
--- /dev/null
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.animation;
+
+import android.annotation.FloatRange;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import java.util.concurrent.Executor;
+
+/** An interface representing an UI component on the display. */
+public interface UIComponent {
+
+    /** Get the current alpha of this UI. */
+    float getAlpha();
+
+    /** Check if this UI is visible. */
+    boolean isVisible();
+
+    /** Get the bounds of this UI in its display. */
+    Rect getBounds();
+
+    /** Create a new {@link Transaction} that can update this UI. */
+    Transaction newTransaction();
+
+    /**
+     * A transaction class for updating {@link UIComponent}.
+     *
+     * @param <T> the subtype of {@link UIComponent} that this {@link Transaction} can handle.
+     */
+    interface Transaction<T extends UIComponent> {
+        /** Update alpha of an UI. Execution will be delayed until {@link #commit()} is called. */
+        Transaction setAlpha(T ui, @FloatRange(from = 0.0, to = 1.0) float alpha);
+
+        /**
+         * Update visibility of an UI. Execution will be delayed until {@link #commit()} is called.
+         */
+        Transaction setVisible(T ui, boolean visible);
+
+        /** Update bounds of an UI. Execution will be delayed until {@link #commit()} is called. */
+        Transaction setBounds(T ui, Rect bounds);
+
+        /**
+         * Attach a ui to the transition leash. Execution will be delayed until {@link #commit()} is
+         * called.
+         */
+        Transaction attachToTransitionLeash(T ui, SurfaceControl transitionLeash, int w, int h);
+
+        /**
+         * Detach a ui from the transition leash. Execution will be delayed until {@link #commit} is
+         * called.
+         */
+        Transaction detachFromTransitionLeash(T ui, Executor executor, Runnable onDone);
+
+        /** Commit any pending changes added to this transaction. */
+        void commit();
+    }
+}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
new file mode 100644
index 0000000..313789c
--- /dev/null
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -0,0 +1,278 @@
+/*
+ * 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.animation;
+
+import android.annotation.Nullable;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A {@link UIComponent} wrapping a {@link View}. After being attached to the transition leash, this
+ * class will draw the content of the {@link View} directly into the leash, and the actual View will
+ * be changed to INVISIBLE in its view tree. This allows the {@link View} to transform in the
+ * full-screen size leash without being constrained by the view tree's boundary or inheriting its
+ * parent's alpha and transformation.
+ */
+public class ViewUIComponent implements UIComponent {
+    private static final String TAG = "ViewUIComponent";
+    private static final boolean DEBUG = Build.IS_USERDEBUG || Log.isLoggable(TAG, Log.DEBUG);
+    private final OnDrawListener mOnDrawListener = this::postDraw;
+    private final View mView;
+
+    @Nullable private SurfaceControl mSurfaceControl;
+    @Nullable private Surface mSurface;
+    @Nullable private Rect mViewBoundsOverride;
+    private boolean mVisibleOverride;
+    private boolean mDirty;
+
+    public ViewUIComponent(View view) {
+        mView = view;
+    }
+
+    @Override
+    public float getAlpha() {
+        return mView.getAlpha();
+    }
+
+    @Override
+    public boolean isVisible() {
+        return isAttachedToLeash() ? mVisibleOverride : mView.getVisibility() == View.VISIBLE;
+    }
+
+    @Override
+    public Rect getBounds() {
+        if (isAttachedToLeash() && mViewBoundsOverride != null) {
+            return mViewBoundsOverride;
+        }
+        return getRealBounds();
+    }
+
+    @Override
+    public Transaction newTransaction() {
+        return new Transaction();
+    }
+
+    private void attachToTransitionLeash(SurfaceControl transitionLeash, int w, int h) {
+        logD("attachToTransitionLeash");
+        // Remember current visibility.
+        mVisibleOverride = mView.getVisibility() == View.VISIBLE;
+
+        // Create the surface
+        mSurfaceControl =
+                new SurfaceControl.Builder().setName("ViewUIComponent").setBufferSize(w, h).build();
+        mSurface = new Surface(mSurfaceControl);
+        forceDraw();
+
+        // Attach surface to transition leash
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.reparent(mSurfaceControl, transitionLeash).show(mSurfaceControl);
+
+        // Make sure view draw triggers surface draw.
+        mView.getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+
+        // Make the view invisible AFTER the surface is shown.
+        t.addTransactionCommittedListener(
+                        mView.getContext().getMainExecutor(),
+                        () -> mView.setVisibility(View.INVISIBLE))
+                .apply();
+    }
+
+    private void detachFromTransitionLeash(Executor executor, Runnable onDone) {
+        logD("detachFromTransitionLeash");
+        Surface s = mSurface;
+        SurfaceControl sc = mSurfaceControl;
+        mSurface = null;
+        mSurfaceControl = null;
+        mView.getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
+        // Restore view visibility
+        mView.setVisibility(mVisibleOverride ? View.VISIBLE : View.INVISIBLE);
+        mView.invalidate();
+        // Clean up surfaces.
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.reparent(sc, null)
+                .addTransactionCommittedListener(
+                        mView.getContext().getMainExecutor(),
+                        () -> {
+                            s.release();
+                            sc.release();
+                            executor.execute(onDone);
+                        });
+        // Apply transaction AFTER the view is drawn.
+        mView.getRootSurfaceControl().applyTransactionOnDraw(t);
+    }
+
+    @Override
+    public String toString() {
+        return "ViewUIComponent{"
+                + "alpha="
+                + getAlpha()
+                + ", visible="
+                + isVisible()
+                + ", bounds="
+                + getBounds()
+                + ", attached="
+                + isAttachedToLeash()
+                + "}";
+    }
+
+    private void draw() {
+        if (!mDirty) {
+            // No need to draw. This is probably a duplicate call.
+            logD("draw: skipped - clean");
+            return;
+        }
+        mDirty = false;
+        if (!isAttachedToLeash()) {
+            // Not attached.
+            logD("draw: skipped - not attached");
+            return;
+        }
+        ViewGroup.LayoutParams params = mView.getLayoutParams();
+        if (params == null || params.width == 0 || params.height == 0) {
+            // layout pass didn't happen.
+            logD("draw: skipped - no layout");
+            return;
+        }
+        Canvas canvas = mSurface.lockHardwareCanvas();
+        // Clear the canvas first.
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        if (mVisibleOverride) {
+            Rect realBounds = getRealBounds();
+            Rect renderBounds = getBounds();
+            canvas.translate(renderBounds.left, renderBounds.top);
+            canvas.scale(
+                    (float) renderBounds.width() / realBounds.width(),
+                    (float) renderBounds.height() / realBounds.height());
+            canvas.saveLayerAlpha(null, (int) (255 * mView.getAlpha()));
+            mView.draw(canvas);
+            canvas.restore();
+        }
+        mSurface.unlockCanvasAndPost(canvas);
+        logD("draw: done");
+    }
+
+    private void forceDraw() {
+        mDirty = true;
+        draw();
+    }
+
+    private Rect getRealBounds() {
+        Rect output = new Rect();
+        mView.getBoundsOnScreen(output);
+        return output;
+    }
+
+    private boolean isAttachedToLeash() {
+        return mSurfaceControl != null && mSurface != null;
+    }
+
+    private void logD(String msg) {
+        if (DEBUG) {
+            Log.d(TAG, msg);
+        }
+    }
+
+    private void setVisible(boolean visible) {
+        logD("setVisibility: " + visible);
+        if (isAttachedToLeash()) {
+            mVisibleOverride = visible;
+            postDraw();
+        } else {
+            mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    private void setBounds(Rect bounds) {
+        logD("setBounds: " + bounds);
+        mViewBoundsOverride = bounds;
+        if (isAttachedToLeash()) {
+            postDraw();
+        } else {
+            Log.w(TAG, "setBounds: not attached to leash!");
+        }
+    }
+
+    private void setAlpha(float alpha) {
+        logD("setAlpha: " + alpha);
+        mView.setAlpha(alpha);
+        if (isAttachedToLeash()) {
+            postDraw();
+        }
+    }
+
+    private void postDraw() {
+        if (mDirty) {
+            return;
+        }
+        mDirty = true;
+        mView.post(this::draw);
+    }
+
+    public static class Transaction implements UIComponent.Transaction<ViewUIComponent> {
+        private final List<Runnable> mChanges = new ArrayList<>();
+
+        @Override
+        public Transaction setAlpha(ViewUIComponent ui, float alpha) {
+            mChanges.add(() -> ui.setAlpha(alpha));
+            return this;
+        }
+
+        @Override
+        public Transaction setVisible(ViewUIComponent ui, boolean visible) {
+            mChanges.add(() -> ui.setVisible(visible));
+            return this;
+        }
+
+        @Override
+        public Transaction setBounds(ViewUIComponent ui, Rect bounds) {
+            mChanges.add(() -> ui.setBounds(bounds));
+            return this;
+        }
+
+        @Override
+        public Transaction attachToTransitionLeash(
+                ViewUIComponent ui, SurfaceControl transitionLeash, int w, int h) {
+            mChanges.add(() -> ui.attachToTransitionLeash(transitionLeash, w, h));
+            return this;
+        }
+
+        @Override
+        public Transaction detachFromTransitionLeash(
+                ViewUIComponent ui, Executor executor, Runnable onDone) {
+            mChanges.add(() -> ui.detachFromTransitionLeash(executor, onDone));
+            return this;
+        }
+
+        @Override
+        public void commit() {
+            mChanges.forEach(Runnable::run);
+            mChanges.clear();
+        }
+    }
+}
diff --git a/packages/SystemUI/animation/lib/tests/Android.bp b/packages/SystemUI/animation/lib/tests/Android.bp
new file mode 100644
index 0000000..c1a3e84
--- /dev/null
+++ b/packages/SystemUI/animation/lib/tests/Android.bp
@@ -0,0 +1,47 @@
+// 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 {
+    default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+    default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_test {
+    name: "PlatformAnimationLibCoreTests",
+
+    defaults: [
+        "platform_app_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    dxflags: ["--multi-dex"],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+    static_libs: [
+        "PlatformAnimationLib-core",
+        "platform-test-rules",
+        "testables",
+    ],
+    compile_multilib: "both",
+    libs: [
+        "android.test.runner.stubs.system",
+        "android.test.base.stubs.system",
+    ],
+
+    certificate: "platform",
+
+    manifest: "AndroidManifest.xml",
+}
diff --git a/packages/SystemUI/animation/lib/tests/AndroidManifest.xml b/packages/SystemUI/animation/lib/tests/AndroidManifest.xml
new file mode 100644
index 0000000..1788abf
--- /dev/null
+++ b/packages/SystemUI/animation/lib/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:sharedUserId="android.uid.system"
+    package="com.android.systemui.animation.core.tests" >
+
+     <application android:debuggable="true" android:testOnly="true">
+         <uses-library android:name="android.test.runner" />
+     </application>
+
+    <instrumentation android:name="android.testing.TestableInstrumentation"
+        android:targetPackage="com.android.systemui.animation.core.tests"
+        android:label="Tests for PlatformAnimationLib-core" />
+</manifest>
diff --git a/packages/SystemUI/animation/lib/tests/AndroidTest.xml b/packages/SystemUI/animation/lib/tests/AndroidTest.xml
new file mode 100644
index 0000000..0f37d7a
--- /dev/null
+++ b/packages/SystemUI/animation/lib/tests/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<configuration description="Runs Tests for PlatformAnimationLib-core.">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="PlatformAnimationLibCoreTests.apk" />
+        <option name="install-arg" value="-t" />
+    </target_preparer>
+
+    <!-- Among other reasons, root access is needed for screen recording artifacts. -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+        <option name="force-root" value="true" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.systemui.animation.core.tests" />
+        <option name="runner" value="android.testing.TestableInstrumentation" />
+        <option name="test-filter-dir" value="/data/data/com.android.systemui.animation.core.tests" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/data/user/0/com.android.systemui.animation.core.tests/files" />
+        <option name="collect-on-run-ended-only" value="false" />
+    </metrics_collector>
+</configuration>
diff --git a/packages/SystemUI/animation/lib/tests/src/com/android/systemui/animation/OriginTransitionSessionTest.java b/packages/SystemUI/animation/lib/tests/src/com/android/systemui/animation/OriginTransitionSessionTest.java
new file mode 100644
index 0000000..287e53b
--- /dev/null
+++ b/packages/SystemUI/animation/lib/tests/src/com/android/systemui/animation/OriginTransitionSessionTest.java
@@ -0,0 +1,406 @@
+/*
+ * 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.animation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.RemoteTransition;
+import android.window.TransitionInfo;
+import android.window.WindowAnimationState;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.systemui.animation.shared.IOriginTransitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Map;
+import java.util.function.Predicate;
+
+/** Unit tests for {@link OriginTransitionSession}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public final class OriginTransitionSessionTest {
+    private static final ComponentName TEST_ACTIVITY_1 = new ComponentName("test", "Activity1");
+    private static final ComponentName TEST_ACTIVITY_2 = new ComponentName("test", "Activity2");
+    private static final ComponentName TEST_ACTIVITY_3 = new ComponentName("test", "Activity3");
+
+    private FakeIOriginTransitions mIOriginTransitions;
+    private Instrumentation mInstrumentation;
+    private FakeIntentStarter mIntentStarter;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
+        mIOriginTransitions = new FakeIOriginTransitions();
+        mIntentStarter = new FakeIntentStarter(TEST_ACTIVITY_1, TEST_ACTIVITY_2);
+    }
+
+    @Test
+    public void sessionStart_withEntryAndExitTransition_transitionsPlayed() {
+        FakeRemoteTransition entry = new FakeRemoteTransition();
+        FakeRemoteTransition exit = new FakeRemoteTransition();
+        OriginTransitionSession session =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(mIntentStarter)
+                        .withEntryTransition(entry)
+                        .withExitTransition(exit)
+                        .build();
+
+        session.start();
+
+        assertThat(mIntentStarter.hasLaunched()).isTrue();
+        assertThat(entry.started()).isTrue();
+
+        runReturnTransition(mIntentStarter);
+
+        assertThat(exit.started()).isTrue();
+    }
+
+    @Test
+    public void sessionStart_withEntryTransition_transitionPlayed() {
+        FakeRemoteTransition entry = new FakeRemoteTransition();
+        OriginTransitionSession session =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(mIntentStarter)
+                        .withEntryTransition(entry)
+                        .build();
+
+        session.start();
+
+        assertThat(mIntentStarter.hasLaunched()).isTrue();
+        assertThat(entry.started()).isTrue();
+        assertThat(mIOriginTransitions.hasPendingReturnTransitions()).isFalse();
+    }
+
+    @Test
+    public void sessionStart_withoutTransition_launchedIntent() {
+        OriginTransitionSession session =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(mIntentStarter)
+                        .build();
+
+        session.start();
+
+        assertThat(mIntentStarter.hasLaunched()).isTrue();
+        assertThat(mIOriginTransitions.hasPendingReturnTransitions()).isFalse();
+    }
+
+    @Test
+    public void sessionStart_cancelledByIntentStarter_transitionNotPlayed() {
+        FakeRemoteTransition entry = new FakeRemoteTransition();
+        FakeRemoteTransition exit = new FakeRemoteTransition();
+        mIntentStarter =
+                new FakeIntentStarter(TEST_ACTIVITY_1, TEST_ACTIVITY_2, /* result= */ false);
+        OriginTransitionSession session =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(mIntentStarter)
+                        .withEntryTransition(entry)
+                        .withExitTransition(exit)
+                        .build();
+
+        session.start();
+
+        assertThat(mIntentStarter.hasLaunched()).isFalse();
+        assertThat(entry.started()).isFalse();
+        assertThat(exit.started()).isFalse();
+        assertThat(mIOriginTransitions.hasPendingReturnTransitions()).isFalse();
+    }
+
+    @Test
+    public void sessionStart_alreadyStarted_noOp() {
+        FakeRemoteTransition entry = new FakeRemoteTransition();
+        FakeRemoteTransition exit = new FakeRemoteTransition();
+        OriginTransitionSession session =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(mIntentStarter)
+                        .withEntryTransition(entry)
+                        .withExitTransition(exit)
+                        .build();
+        session.start();
+        entry.reset();
+        mIntentStarter.reset();
+
+        session.start();
+
+        assertThat(mIntentStarter.hasLaunched()).isFalse();
+        assertThat(entry.started()).isFalse();
+    }
+
+    @Test
+    public void sessionStart_alreadyCancelled_noOp() {
+        FakeRemoteTransition entry = new FakeRemoteTransition();
+        FakeRemoteTransition exit = new FakeRemoteTransition();
+        OriginTransitionSession session =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(mIntentStarter)
+                        .withEntryTransition(entry)
+                        .withExitTransition(exit)
+                        .build();
+        session.cancel();
+
+        session.start();
+
+        assertThat(mIntentStarter.hasLaunched()).isFalse();
+        assertThat(entry.started()).isFalse();
+        assertThat(mIOriginTransitions.hasPendingReturnTransitions()).isFalse();
+    }
+
+    @Test
+    public void sessionCancelled_returnTransitionNotPlayed() {
+        FakeRemoteTransition entry = new FakeRemoteTransition();
+        FakeRemoteTransition exit = new FakeRemoteTransition();
+        OriginTransitionSession session =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(mIntentStarter)
+                        .withEntryTransition(entry)
+                        .withExitTransition(exit)
+                        .build();
+
+        session.start();
+        session.cancel();
+
+        assertThat(mIOriginTransitions.hasPendingReturnTransitions()).isFalse();
+    }
+
+    @Test
+    public void multipleSessionsStarted_allTransitionsPlayed() {
+        FakeRemoteTransition entry1 = new FakeRemoteTransition();
+        FakeRemoteTransition exit1 = new FakeRemoteTransition();
+        FakeIntentStarter starter1 = mIntentStarter;
+        OriginTransitionSession session1 =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(starter1)
+                        .withEntryTransition(entry1)
+                        .withExitTransition(exit1)
+                        .build();
+        FakeRemoteTransition entry2 = new FakeRemoteTransition();
+        FakeRemoteTransition exit2 = new FakeRemoteTransition();
+        FakeIntentStarter starter2 = new FakeIntentStarter(TEST_ACTIVITY_2, TEST_ACTIVITY_3);
+        OriginTransitionSession session2 =
+                new OriginTransitionSession.Builder(mContext, mIOriginTransitions)
+                        .withIntentStarter(starter2)
+                        .withEntryTransition(entry2)
+                        .withExitTransition(exit2)
+                        .build();
+
+        session1.start();
+
+        assertThat(starter1.hasLaunched()).isTrue();
+        assertThat(entry1.started()).isTrue();
+
+        session2.start();
+
+        assertThat(starter2.hasLaunched()).isTrue();
+        assertThat(entry2.started()).isTrue();
+
+        runReturnTransition(starter2);
+
+        assertThat(exit2.started()).isTrue();
+
+        runReturnTransition(starter1);
+
+        assertThat(exit1.started()).isTrue();
+    }
+
+    private void runReturnTransition(FakeIntentStarter intentStarter) {
+        TransitionInfo info =
+                buildTransitionInfo(intentStarter.getToActivity(), intentStarter.getFromActivity());
+        mIOriginTransitions.runReturnTransition(intentStarter.getTransitionOfLastLaunch(), info);
+    }
+
+    private static TransitionInfo buildTransitionInfo(ComponentName from, ComponentName to) {
+        TransitionInfo info = new TransitionInfo(WindowManager.TRANSIT_OPEN, /* flags= */ 0);
+        TransitionInfo.Change c1 =
+                new TransitionInfo.Change(/* container= */ null, /* leash= */ null);
+        c1.setMode(WindowManager.TRANSIT_OPEN);
+        c1.setActivityComponent(to);
+        TransitionInfo.Change c2 =
+                new TransitionInfo.Change(/* container= */ null, /* leash= */ null);
+        c2.setMode(WindowManager.TRANSIT_CLOSE);
+        c2.setActivityComponent(from);
+        info.addChange(c2);
+        info.addChange(c1);
+        return info;
+    }
+
+    private static class FakeIntentStarter implements Predicate<RemoteTransition> {
+        private final ComponentName mFromActivity;
+        private final ComponentName mToActivity;
+        private final boolean mResult;
+
+        @Nullable private RemoteTransition mTransition;
+        private boolean mLaunched;
+
+        FakeIntentStarter(ComponentName from, ComponentName to) {
+            this(from, to, /* result= */ true);
+        }
+
+        FakeIntentStarter(ComponentName from, ComponentName to, boolean result) {
+            mFromActivity = from;
+            mToActivity = to;
+            mResult = result;
+        }
+
+        @Override
+        public boolean test(RemoteTransition transition) {
+            if (mResult) {
+                mLaunched = true;
+                mTransition = transition;
+                if (mTransition != null) {
+                    TransitionInfo info = buildTransitionInfo(mFromActivity, mToActivity);
+                    try {
+                        transition
+                                .getRemoteTransition()
+                                .startAnimation(
+                                        new Binder(),
+                                        info,
+                                        new SurfaceControl.Transaction(),
+                                        new FakeFinishCallback());
+                    } catch (RemoteException e) {
+
+                    }
+                }
+            }
+            return mResult;
+        }
+
+        @Nullable
+        public RemoteTransition getTransitionOfLastLaunch() {
+            return mTransition;
+        }
+
+        public ComponentName getFromActivity() {
+            return mFromActivity;
+        }
+
+        public ComponentName getToActivity() {
+            return mToActivity;
+        }
+
+        public boolean hasLaunched() {
+            return mLaunched;
+        }
+
+        public void reset() {
+            mTransition = null;
+            mLaunched = false;
+        }
+    }
+
+    private static class FakeIOriginTransitions extends IOriginTransitions.Stub {
+        private final Map<RemoteTransition, RemoteTransition> mRecords = new ArrayMap<>();
+
+        @Override
+        public RemoteTransition makeOriginTransition(
+                RemoteTransition launchTransition, RemoteTransition returnTransition) {
+            mRecords.put(launchTransition, returnTransition);
+            return launchTransition;
+        }
+
+        @Override
+        public void cancelOriginTransition(RemoteTransition originTransition) {
+            mRecords.remove(originTransition);
+        }
+
+        public void runReturnTransition(RemoteTransition originTransition, TransitionInfo info) {
+            RemoteTransition transition = mRecords.remove(originTransition);
+            try {
+                transition
+                        .getRemoteTransition()
+                        .startAnimation(
+                                new Binder(),
+                                info,
+                                new SurfaceControl.Transaction(),
+                                new FakeFinishCallback());
+            } catch (RemoteException e) {
+
+            }
+        }
+
+        public boolean hasPendingReturnTransitions() {
+            return !mRecords.isEmpty();
+        }
+    }
+
+    private static class FakeFinishCallback extends IRemoteTransitionFinishedCallback.Stub {
+        @Override
+        public void onTransitionFinished(
+                WindowContainerTransaction wct, SurfaceControl.Transaction sct) {}
+    }
+
+    private static class FakeRemoteTransition extends IRemoteTransition.Stub {
+        private boolean mStarted;
+
+        @Override
+        public void startAnimation(
+                IBinder token,
+                TransitionInfo info,
+                SurfaceControl.Transaction t,
+                IRemoteTransitionFinishedCallback finishCallback)
+                throws RemoteException {
+            mStarted = true;
+            finishCallback.onTransitionFinished(null, null);
+        }
+
+        @Override
+        public void mergeAnimation(
+                IBinder transition,
+                TransitionInfo info,
+                SurfaceControl.Transaction t,
+                IBinder mergeTarget,
+                IRemoteTransitionFinishedCallback finishCallback) {}
+
+        @Override
+        public void takeOverAnimation(
+                IBinder transition,
+                TransitionInfo info,
+                SurfaceControl.Transaction t,
+                IRemoteTransitionFinishedCallback finishCallback,
+                WindowAnimationState[] states) {}
+
+        @Override
+        public void onTransitionConsumed(IBinder transition, boolean aborted) {}
+
+        public boolean started() {
+            return mStarted;
+        }
+
+        public void reset() {
+            mStarted = false;
+        }
+    }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
index 907c39d..f5d01d7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
@@ -944,26 +944,9 @@
                 }
 
                 override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
-                    // onLaunchAnimationEnd is called by an Animator at the end of the animation,
-                    // on a Choreographer animation tick. The following calls will move the animated
-                    // content from the dialog overlay back to its original position, and this
-                    // change must be reflected in the next frame given that we then sync the next
-                    // frame of both the content and dialog ViewRoots. However, in case that content
-                    // is rendered by Compose, whose compositions are also scheduled on a
-                    // Choreographer frame, any state change made *right now* won't be reflected in
-                    // the next frame given that a Choreographer frame can't schedule another and
-                    // have it happen in the same frame. So we post the forwarded calls to
-                    // [Controller.onLaunchAnimationEnd], leaving this Choreographer frame, ensuring
-                    // that the move of the content back to its original window will be reflected in
-                    // the next frame right after [onLaunchAnimationEnd] is called.
-                    //
-                    // TODO(b/330672236): Move this to TransitionAnimator.
-                    dialog.context.mainExecutor.execute {
-                        startController.onTransitionAnimationEnd(isExpandingFullyAbove)
-                        endController.onTransitionAnimationEnd(isExpandingFullyAbove)
-
-                        onLaunchAnimationEnd()
-                    }
+                    startController.onTransitionAnimationEnd(isExpandingFullyAbove)
+                    endController.onTransitionAnimationEnd(isExpandingFullyAbove)
+                    onLaunchAnimationEnd()
                 }
 
                 override fun onTransitionAnimationProgress(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index fc4cf1d..859fc4e0 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -379,13 +379,26 @@
                         Log.d(TAG, "Animation ended")
                     }
 
-                    // TODO(b/330672236): Post this to the main thread instead so that it does not
-                    // flicker with Flexiglass enabled.
-                    controller.onTransitionAnimationEnd(isExpandingFullyAbove)
-                    transitionContainerOverlay.remove(windowBackgroundLayer)
+                    // onAnimationEnd is called at the end of the animation, on a Choreographer
+                    // animation tick. During dialog launches, the following calls will move the
+                    // animated content from the dialog overlay back to its original position, and
+                    // this change must be reflected in the next frame given that we then sync the
+                    // next frame of both the content and dialog ViewRoots. During SysUI activity
+                    // launches, we will instantly collapse the shade at the end of the transition.
+                    // However, if those are rendered by Compose, whose compositions are also
+                    // scheduled on a Choreographer frame, any state change made *right now* won't
+                    // be reflected in the next frame given that a Choreographer frame can't
+                    // schedule another and have it happen in the same frame. So we post the
+                    // forwarded calls to [Controller.onLaunchAnimationEnd] in the main executor,
+                    // leaving this Choreographer frame, ensuring that any state change applied by
+                    // onTransitionAnimationEnd() will be reflected in the same frame.
+                    mainExecutor.execute {
+                        controller.onTransitionAnimationEnd(isExpandingFullyAbove)
+                        transitionContainerOverlay.remove(windowBackgroundLayer)
 
-                    if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
-                        openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+                        if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
+                            openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+                        }
                     }
                 }
             }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index a5f8057..20efea5 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -28,11 +28,11 @@
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.IconButtonColors
 import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.unit.dp
-import com.android.compose.theme.LocalAndroidColorScheme
 
 @Composable
 fun PlatformButton(
@@ -100,12 +100,7 @@
     @DrawableRes iconResource: Int,
     contentDescription: String?,
 ) {
-    IconButton(
-        modifier = modifier,
-        onClick = onClick,
-        enabled = enabled,
-        colors = colors,
-    ) {
+    IconButton(modifier = modifier, onClick = onClick, enabled = enabled, colors = colors) {
         Icon(
             painter = painterResource(id = iconResource),
             contentDescription = contentDescription,
@@ -118,7 +113,7 @@
 
 @Composable
 private fun filledButtonColors(): ButtonColors {
-    val colors = LocalAndroidColorScheme.current
+    val colors = MaterialTheme.colorScheme
     return ButtonDefaults.buttonColors(
         containerColor = colors.primary,
         contentColor = colors.onPrimary,
@@ -127,27 +122,22 @@
 
 @Composable
 private fun outlineButtonColors(): ButtonColors {
-    return ButtonDefaults.outlinedButtonColors(
-        contentColor = LocalAndroidColorScheme.current.onSurface,
-    )
+    return ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.onSurface)
 }
 
 @Composable
 private fun iconButtonColors(): IconButtonColors {
     return IconButtonDefaults.filledIconButtonColors(
-        contentColor = LocalAndroidColorScheme.current.onSurface,
+        contentColor = MaterialTheme.colorScheme.onSurface
     )
 }
 
 @Composable
 private fun outlineButtonBorder(): BorderStroke {
-    return BorderStroke(
-        width = 1.dp,
-        color = LocalAndroidColorScheme.current.primary,
-    )
+    return BorderStroke(width = 1.dp, color = MaterialTheme.colorScheme.primary)
 }
 
 @Composable
 private fun textButtonColors(): ButtonColors {
-    return ButtonDefaults.textButtonColors(contentColor = LocalAndroidColorScheme.current.primary)
+    return ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.primary)
 }
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 bfeaf92..0f6e6a7 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
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.scene.ui.composable.Scene
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -35,12 +36,7 @@
 import javax.inject.Provider
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@Module(
-    includes =
-        [
-            LockscreenSceneBlueprintModule::class,
-        ],
-)
+@Module(includes = [LockscreenSceneBlueprintModule::class])
 interface LockscreenSceneModule {
 
     @Binds @IntoSet fun lockscreenScene(scene: LockscreenScene): Scene
@@ -51,9 +47,7 @@
         @Provides
         @SysUISingleton
         @KeyguardRootView
-        fun viewProvider(
-            configurator: Provider<KeyguardViewConfigurator>,
-        ): () -> View {
+        fun viewProvider(configurator: Provider<KeyguardViewConfigurator>): () -> View {
             return { configurator.get().getKeyguardRootView() }
         }
 
@@ -67,10 +61,16 @@
         @Provides
         fun providesLockscreenContent(
             viewModelFactory: LockscreenContentViewModel.Factory,
+            notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory,
             blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>,
             clockInteractor: KeyguardClockInteractor,
         ): LockscreenContent {
-            return LockscreenContent(viewModelFactory, blueprints, clockInteractor)
+            return LockscreenContent(
+                viewModelFactory,
+                notificationScrimViewModelFactory,
+                blueprints,
+                clockInteractor,
+            )
         }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt
index 0b96694..69ca0a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt
@@ -38,7 +38,6 @@
 import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
-import com.android.compose.theme.LocalAndroidColorScheme
 import kotlin.math.roundToInt
 
 /**
@@ -69,7 +68,7 @@
                 Modifier.defaultMinSize(minWidth = defaultSize, minHeight = defaultSize),
                 propagateMinConstraints = true,
             ) {
-                val iconColor = LocalAndroidColorScheme.current.primary
+                val iconColor = MaterialTheme.colorScheme.primary
                 CompositionLocalProvider(LocalContentColor provides iconColor) { icon() }
             }
 
@@ -77,7 +76,7 @@
         }
 
         // Title.
-        val titleColor = LocalAndroidColorScheme.current.onSurface
+        val titleColor = MaterialTheme.colorScheme.onSurface
         CompositionLocalProvider(LocalContentColor provides titleColor) {
             ProvideTextStyle(
                 MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
@@ -88,7 +87,7 @@
         Spacer(Modifier.height(16.dp))
 
         // Content.
-        val contentColor = LocalAndroidColorScheme.current.onSurfaceVariant
+        val contentColor = MaterialTheme.colorScheme.onSurfaceVariant
         Box {
             CompositionLocalProvider(LocalContentColor provides contentColor) {
                 ProvideTextStyle(
@@ -169,7 +168,7 @@
                                 negative.width -
                                 positive.width -
                                 horizontalSpacing.roundToInt(),
-                            0
+                            0,
                         )
                     }
                 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index dbe7538..5c5514a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -30,6 +30,8 @@
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.notifications.ui.composable.NotificationLockscreenScrim
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel
 
 /**
  * Renders the content of the lockscreen.
@@ -39,6 +41,7 @@
  */
 class LockscreenContent(
     private val viewModelFactory: LockscreenContentViewModel.Factory,
+    private val notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory,
     private val blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>,
     private val clockInteractor: KeyguardClockInteractor,
 ) {
@@ -47,10 +50,13 @@
     }
 
     @Composable
-    fun SceneScope.Content(
-        modifier: Modifier = Modifier,
-    ) {
-        val viewModel = rememberViewModel("LockscreenContent") { viewModelFactory.create() }
+    fun SceneScope.Content(modifier: Modifier = Modifier) {
+        val viewModel =
+            rememberViewModel("LockscreenContent-viewModel") { viewModelFactory.create() }
+        val notificationLockscreenScrimViewModel =
+            rememberViewModel("LockscreenContent-scrimViewModel") {
+                notificationScrimViewModelFactory.create()
+            }
         val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle()
         if (!isContentVisible) {
             // If the content isn't supposed to be visible, show a large empty box as it's needed
@@ -71,6 +77,9 @@
         }
 
         val blueprint = blueprintByBlueprintId[blueprintId] ?: return
-        with(blueprint) { Content(viewModel, modifier.sysuiResTag("keyguard_root_view")) }
+        with(blueprint) {
+            Content(viewModel, modifier.sysuiResTag("keyguard_root_view"))
+            NotificationLockscreenScrim(notificationLockscreenScrimViewModel)
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index aaf49ff..9444664 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -42,8 +42,9 @@
     isClock: Boolean = false,
 ): Modifier {
     val translationYState = remember { mutableStateOf(0F) }
-    val copiedParams = params.copy(translationY = { translationYState.value })
-    val burnIn = viewModel.movement(copiedParams)
+    viewModel.updateBurnInParams(params.copy(translationY = { translationYState.value }))
+
+    val burnIn = viewModel.movement
     val translationX by
         burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
     val translationY by
@@ -51,12 +52,7 @@
     translationYState.value = translationY
     val scaleViewModel by
         burnIn
-            .map {
-                BurnInScaleViewModel(
-                    scale = it.scale,
-                    scaleClockOnly = it.scaleClockOnly,
-                )
-            }
+            .map { BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly) }
             .collectAsStateWithLifecycle(initialValue = BurnInScaleViewModel())
 
     return this.graphicsLayer {
@@ -72,8 +68,6 @@
 
 /** Reports the "top" coordinate of the modified composable to the given [consumer]. */
 @Composable
-fun Modifier.onTopPlacementChanged(
-    consumer: (Float) -> Unit,
-): Modifier {
+fun Modifier.onTopPlacementChanged(consumer: (Float) -> Unit): Modifier {
     return onPlaced { coordinates -> consumer(coordinates.boundsInWindow().top) }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index f4d9e82..3d8ca1e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -22,7 +22,9 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.layout.layout
@@ -31,8 +33,12 @@
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.MovableElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.internal.R.attr.layout
+import com.android.systemui.media.controls.ui.composable.MediaCarouselStateLoader.stateForMediaCarouselContent
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
 import com.android.systemui.res.R
 import com.android.systemui.util.animation.MeasurementInput
 
@@ -53,12 +59,20 @@
     modifier: Modifier = Modifier,
     carouselController: MediaCarouselController,
     offsetProvider: (() -> IntOffset)? = null,
+    usingCollapsedLandscapeMedia: Boolean = false,
 ) {
     if (!isVisible || carouselController.isLockedAndHidden()) {
         return
     }
 
-    val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
+    val carouselState = remember { { stateForMediaCarouselContent() } }
+    val isCollapsed = usingCollapsedLandscapeMedia && isLandscape()
+    val mediaHeight =
+        if (isCollapsed && mediaHost.expansion == MediaHostState.COLLAPSED) {
+            dimensionResource(R.dimen.qs_media_session_height_collapsed)
+        } else {
+            dimensionResource(R.dimen.qs_media_session_height_expanded)
+        }
 
     MovableElement(
         key = MediaCarousel.Elements.Content,
@@ -95,6 +109,7 @@
                             }
                         },
                 factory = { context ->
+                    MediaCarouselStateLoader.loadCarouselState(carouselController, carouselState())
                     FrameLayout(context).apply {
                         layoutParams =
                             FrameLayout.LayoutParams(
@@ -103,7 +118,10 @@
                             )
                     }
                 },
-                update = { it.setView(carouselController.mediaFrame) },
+                update = {
+                    MediaCarouselStateLoader.loadCarouselState(carouselController, carouselState())
+                    it.setView(carouselController.mediaFrame)
+                },
                 onRelease = { it.removeAllViews() },
             )
         }
@@ -117,3 +135,8 @@
     (view.parent as? ViewGroup)?.removeView(view)
     addView(view)
 }
+
+@Composable
+fun SceneScope.isLandscape(): Boolean {
+    return LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
new file mode 100644
index 0000000..4a0136c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarouselStateLoader.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.media.controls.ui.composable
+
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.Scenes
+import kotlin.math.min
+
+object MediaCarouselStateLoader {
+
+    /** Sets current state for media carousel. */
+    fun loadCarouselState(carouselController: MediaCarouselController, state: State) {
+        if (state is State.Gone) return
+
+        carouselController.setCurrentState(
+            state.startLocation,
+            state.endLocation,
+            state.transitionProgress,
+            immediately = true,
+        )
+    }
+
+    /** Returns the corresponding media location for the given [scene] */
+    @MediaLocation
+    private fun getMediaLocation(scene: SceneKey): Int {
+        return when (scene) {
+            Scenes.QuickSettings -> MediaHierarchyManager.LOCATION_QS
+            Scenes.Shade -> MediaHierarchyManager.LOCATION_QQS
+            Scenes.Lockscreen -> MediaHierarchyManager.LOCATION_LOCKSCREEN
+            Scenes.Communal -> MediaHierarchyManager.LOCATION_COMMUNAL_HUB
+            else -> MediaHierarchyManager.LOCATION_UNKNOWN
+        }
+    }
+
+    /** Returns the corresponding media location for the given [content] */
+    @MediaLocation
+    private fun getMediaLocation(content: ContentKey): Int {
+        return when (content) {
+            Overlays.QuickSettingsShade -> MediaHierarchyManager.LOCATION_QS
+            Overlays.NotificationsShade -> MediaHierarchyManager.LOCATION_QQS
+            else -> MediaHierarchyManager.LOCATION_UNKNOWN
+        }
+    }
+
+    /** State for media carousel. */
+    sealed interface State {
+        val transitionProgress: Float
+        // TODO b/368368388: implement media squishiness
+        val squishFraction: () -> Float
+        @MediaLocation val startLocation: Int
+        @MediaLocation val endLocation: Int
+
+        /** State when media carousel is not visible on screen. */
+        data object Gone : State {
+            override val transitionProgress: Float = 1.0F
+            override val squishFraction: () -> Float = { 1.0F }
+            override val endLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
+            override val startLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
+        }
+
+        /** State when media carousel is moving from one media location to another */
+        data class InProgress(
+            override val transitionProgress: Float,
+            override val startLocation: Int,
+            override val endLocation: Int,
+        ) : State {
+            override val squishFraction = { 1.0F }
+        }
+
+        /** State when media carousel reached the end location. */
+        data class Idle(override val endLocation: Int) : State {
+            override val transitionProgress = 1.0F
+            override val startLocation = MediaHierarchyManager.LOCATION_UNKNOWN
+            override val squishFraction = { 1.0F }
+        }
+    }
+
+    /** Returns the state of media carousel */
+    fun SceneScope.stateForMediaCarouselContent(): State {
+        return when (val transitionState = layoutState.transitionState) {
+            is TransitionState.Idle -> {
+                if (MediaContentPicker.contents.contains(transitionState.currentScene)) {
+                    State.Idle(getMediaLocation(transitionState.currentScene))
+                } else {
+                    State.Gone
+                }
+            }
+            is TransitionState.Transition.ChangeScene ->
+                with(transitionState) {
+                    if (
+                        MediaContentPicker.contents.contains(toScene) &&
+                            MediaContentPicker.contents.contains(fromScene)
+                    ) {
+                        State.InProgress(
+                            min(progress, 1.0F),
+                            getMediaLocation(fromScene),
+                            getMediaLocation(toScene),
+                        )
+                    } else if (MediaContentPicker.contents.contains(toScene)) {
+                        State.InProgress(
+                            transitionProgress = 1.0F,
+                            startLocation = MediaHierarchyManager.LOCATION_UNKNOWN,
+                            getMediaLocation(toScene),
+                        )
+                    } else {
+                        State.Gone
+                    }
+                }
+            is TransitionState.Transition.OverlayTransition ->
+                with(transitionState) {
+                    if (
+                        MediaContentPicker.contents.contains(toContent) &&
+                            MediaContentPicker.contents.contains(fromContent)
+                    ) {
+                        State.InProgress(
+                            min(progress, 1.0F),
+                            getMediaLocation(fromContent),
+                            getMediaLocation(toContent),
+                        )
+                    } else if (MediaContentPicker.contents.contains(toContent)) {
+                        State.InProgress(
+                            transitionProgress = 1.0F,
+                            startLocation = MediaHierarchyManager.LOCATION_UNKNOWN,
+                            getMediaLocation(toContent),
+                        )
+                    } else {
+                        State.Gone
+                    }
+                }
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
new file mode 100644
index 0000000..4279be3
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationLockscreenScrim.kt
@@ -0,0 +1,119 @@
+/*
+ * 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.composable
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel
+import kotlinx.coroutines.launch
+
+/**
+ * A full-screen notifications scrim that is only visible after transitioning from Shade scene to
+ * Lockscreen Scene and ending user input, at which point it fades out, visually completing the
+ * transition.
+ */
+@Composable
+fun SceneScope.NotificationLockscreenScrim(
+    viewModel: NotificationLockscreenScrimViewModel,
+    modifier: Modifier = Modifier,
+) {
+    val coroutineScope = rememberCoroutineScope()
+    val shadeMode = viewModel.shadeMode.collectAsStateWithLifecycle()
+
+    // Important: Make sure that shouldShowScrimFadeOut() is checked the first time the Lockscreen
+    // scene is composed.
+    val useFadeOutOnComposition =
+        remember(shadeMode.value) {
+            layoutState.currentTransition?.let { currentTransition ->
+                shouldShowScrimFadeOut(currentTransition, shadeMode.value)
+            } ?: false
+        }
+
+    val alphaAnimatable = remember { Animatable(1f) }
+
+    LaunchedEffect(
+        alphaAnimatable,
+        layoutState.currentTransition,
+        useFadeOutOnComposition,
+        shadeMode,
+    ) {
+        val currentTransition = layoutState.currentTransition
+        if (
+            useFadeOutOnComposition &&
+                currentTransition != null &&
+                shouldShowScrimFadeOut(currentTransition, shadeMode.value) &&
+                currentTransition.isUserInputOngoing
+        ) {
+            // keep scrim visible until user lifts their finger.
+            viewModel.setAlphaForLockscreenFadeIn(0f)
+            alphaAnimatable.snapTo(1f)
+        } else if (
+            useFadeOutOnComposition &&
+                (currentTransition == null ||
+                    (shouldShowScrimFadeOut(currentTransition, shadeMode.value) &&
+                        !currentTransition.isUserInputOngoing))
+        ) {
+            // we no longer want to keep the scrim from fading out, so animate the scrim fade-out
+            // and pipe the progress to the view model as well, so NSSL can fade-in the stack in
+            // tandem.
+            viewModel.setAlphaForLockscreenFadeIn(0f)
+            coroutineScope.launch {
+                snapshotFlow { alphaAnimatable.value }
+                    .collect { viewModel.setAlphaForLockscreenFadeIn(1 - it) }
+            }
+            alphaAnimatable.animateTo(0f, tween())
+        } else {
+            // disable the scrim fade logic.
+            viewModel.setAlphaForLockscreenFadeIn(1f)
+            alphaAnimatable.snapTo(0f)
+        }
+    }
+
+    Box(
+        modifier
+            .fillMaxSize()
+            .element(Notifications.Elements.NotificationScrim)
+            .graphicsLayer { alpha = alphaAnimatable.value }
+            .background(MaterialTheme.colorScheme.surface)
+    )
+}
+
+private fun shouldShowScrimFadeOut(
+    currentTransition: TransitionState.Transition,
+    shadeMode: ShadeMode,
+): Boolean {
+    return shadeMode == ShadeMode.Single &&
+        currentTransition.isInitiatedByUserInput &&
+        (currentTransition.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) ||
+            currentTransition.isTransitioning(from = Scenes.Bouncer, to = Scenes.Lockscreen))
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 1b99a96..2066c93 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -86,6 +86,8 @@
 import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayoutState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
 import com.android.systemui.res.R
@@ -101,7 +103,7 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import kotlin.math.roundToInt
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
 object Notifications {
@@ -130,8 +132,8 @@
 fun SceneScope.HeadsUpNotificationSpace(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
+    useHunBounds: () -> Boolean = { true },
     modifier: Modifier = Modifier,
-    isPeekFromBottom: Boolean = false,
 ) {
     Box(
         modifier =
@@ -141,17 +143,25 @@
                 .notificationHeadsUpHeight(stackScrollView)
                 .debugBackground(viewModel, DEBUG_HUN_COLOR)
                 .onGloballyPositioned { coordinates: LayoutCoordinates ->
-                    val positionInWindow = coordinates.positionInWindow()
-                    val boundsInWindow = coordinates.boundsInWindow()
-                    debugLog(viewModel) {
-                        "HUNS onGloballyPositioned:" +
-                            " size=${coordinates.size}" +
-                            " bounds=$boundsInWindow"
+                    // This element is sometimes opted out of the shared element system, so there
+                    // can be multiple instances of it during a transition. Thus we need to
+                    // determine which instance should feed its bounds to NSSL to avoid providing
+                    // conflicting values
+                    val useBounds = useHunBounds()
+                    if (useBounds) {
+                        val positionInWindow = coordinates.positionInWindow()
+                        val boundsInWindow = coordinates.boundsInWindow()
+                        debugLog(viewModel) {
+                            "HUNS onGloballyPositioned:" +
+                                " size=${coordinates.size}" +
+                                " bounds=$boundsInWindow"
+                        }
+                        // Note: boundsInWindow doesn't scroll off the screen, so use
+                        // positionInWindow
+                        // for top bound, which can scroll off screen while snoozing
+                        stackScrollView.setHeadsUpTop(positionInWindow.y)
+                        stackScrollView.setHeadsUpBottom(boundsInWindow.bottom)
                     }
-                    // Note: boundsInWindow doesn't scroll off the screen, so use positionInWindow
-                    // for top bound, which can scroll off screen while snoozing
-                    stackScrollView.setHeadsUpTop(positionInWindow.y)
-                    stackScrollView.setHeadsUpBottom(boundsInWindow.bottom)
                 }
     )
 }
@@ -243,6 +253,7 @@
         NotificationPlaceholder(
             stackScrollView = stackScrollView,
             viewModel = viewModel,
+            useStackBounds = { shouldUseLockscreenStackBounds(layoutState.transitionState) },
             modifier = Modifier.fillMaxSize(),
         )
         HeadsUpNotificationSpace(
@@ -355,7 +366,6 @@
         snapshotFlow { syntheticScroll.value }
             .collect { delta ->
                 scrollNotificationStack(
-                    scope = coroutineScope,
                     delta = delta,
                     animate = false,
                     scrimOffset = scrimOffset,
@@ -375,7 +385,6 @@
                 // composed at least once), and our remote input row overlaps with the ime bounds.
                 if (isRemoteInputActive && imeTopValue > 0f && remoteInputRowBottom > imeTopValue) {
                     scrollNotificationStack(
-                        scope = coroutineScope,
                         delta = remoteInputRowBottom - imeTopValue,
                         animate = true,
                         scrimOffset = scrimOffset,
@@ -442,7 +451,10 @@
                                 scrimCornerRadius,
                                 screenCornerRadius,
                                 { expansionFraction },
-                                shouldPunchHoleBehindScrim,
+                                shouldAnimateScrimCornerRadius(
+                                    layoutState,
+                                    shouldPunchHoleBehindScrim,
+                                ),
                             )
                             .let { scrimRounding.value.toRoundedCornerShape(it) }
                     clip = true
@@ -506,6 +518,9 @@
                 NotificationPlaceholder(
                     stackScrollView = stackScrollView,
                     viewModel = viewModel,
+                    useStackBounds = {
+                        !shouldUseLockscreenStackBounds(layoutState.transitionState)
+                    },
                     modifier =
                         Modifier.notificationStackHeight(
                                 view = stackScrollView,
@@ -592,6 +607,7 @@
 private fun SceneScope.NotificationPlaceholder(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
+    useStackBounds: () -> Boolean,
     modifier: Modifier = Modifier,
 ) {
     Box(
@@ -601,21 +617,26 @@
                 .debugBackground(viewModel, DEBUG_STACK_COLOR)
                 .onSizeChanged { size -> debugLog(viewModel) { "STACK onSizeChanged: size=$size" } }
                 .onGloballyPositioned { coordinates: LayoutCoordinates ->
-                    val positionInWindow = coordinates.positionInWindow()
-                    debugLog(viewModel) {
-                        "STACK onGloballyPositioned:" +
-                            " size=${coordinates.size}" +
-                            " position=$positionInWindow" +
-                            " bounds=${coordinates.boundsInWindow()}"
+                    // This element is opted out of the shared element system, so there can be
+                    // multiple instances of it during a transition. Thus we need to determine which
+                    // instance should feed its bounds to NSSL to avoid providing conflicting values
+                    val useBounds = useStackBounds()
+                    if (useBounds) {
+                        // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top won't
+                        val positionInWindow = coordinates.positionInWindow()
+                        debugLog(viewModel) {
+                            "STACK onGloballyPositioned:" +
+                                " size=${coordinates.size}" +
+                                " position=$positionInWindow" +
+                                " bounds=${coordinates.boundsInWindow()}"
+                        }
+                        stackScrollView.setStackTop(positionInWindow.y)
                     }
-                    // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not
-                    stackScrollView.setStackTop(positionInWindow.y)
                 }
     )
 }
 
 private suspend fun scrollNotificationStack(
-    scope: CoroutineScope,
     delta: Float,
     animate: Boolean,
     scrimOffset: Animatable<Float, AnimationVector1D>,
@@ -630,7 +651,7 @@
             if (animate) {
                 // launch a new coroutine for the remainder animation so that it doesn't suspend the
                 // scrim animation, allowing both to play simultaneously.
-                scope.launch { scrollState.animateScrollTo(remainingDelta) }
+                coroutineScope { launch { scrollState.animateScrollTo(remainingDelta) } }
             } else {
                 scrollState.scrollTo(remainingDelta)
             }
@@ -650,6 +671,18 @@
     }
 }
 
+private fun shouldUseLockscreenStackBounds(state: TransitionState): Boolean {
+    return state is TransitionState.Idle && state.currentScene == Scenes.Lockscreen
+}
+
+private fun shouldAnimateScrimCornerRadius(
+    state: SceneTransitionLayoutState,
+    shouldPunchHoleBehindScrim: Boolean,
+): Boolean {
+    return shouldPunchHoleBehindScrim ||
+        state.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen)
+}
+
 private fun calculateCornerRadius(
     scrimCornerRadius: Dp,
     screenCornerRadius: Dp,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index d91958a..6304979 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -48,7 +48,6 @@
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
@@ -85,6 +84,7 @@
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.composable.isLandscape
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.dagger.MediaModule
@@ -288,9 +288,7 @@
 
         // ############# Media ###############
         val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
-        val mediaInRow =
-            isMediaVisible &&
-                LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
+        val mediaInRow = isMediaVisible && isLandscape()
         val mediaOffset by
             animateSceneDpAsState(value = InQS, key = MediaLandscapeTopOffset, canOverflow = false)
 
@@ -416,11 +414,11 @@
         HeadsUpNotificationSpace(
             stackScrollView = notificationStackScrollView,
             viewModel = notificationsPlaceholderViewModel,
+            useHunBounds = { shouldUseQuickSettingsHunBounds(layoutState.transitionState) },
             modifier =
                 Modifier.align(Alignment.BottomCenter)
                     .navigationBarsPadding()
                     .padding(horizontal = shadeHorizontalPadding),
-            isPeekFromBottom = true,
         )
         NotificationScrollingStack(
             shadeSession = shadeSession,
@@ -446,3 +444,7 @@
         )
     }
 }
+
+private fun shouldUseQuickSettingsHunBounds(state: TransitionState): Boolean {
+    return state is TransitionState.Idle && state.currentScene == Scenes.QuickSettings
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index f8d0588..54497f6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -22,6 +22,7 @@
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -34,6 +35,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.battery.BatteryMeterViewController
@@ -41,6 +43,7 @@
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.composefragment.ui.GridAnchor
 import com.android.systemui.qs.panels.ui.compose.EditMode
 import com.android.systemui.qs.panels.ui.compose.TileGrid
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
@@ -79,16 +82,11 @@
     }
 
     @Composable
-    override fun ContentScope.Content(
-        modifier: Modifier,
-    ) {
+    override fun ContentScope.Content(modifier: Modifier) {
         val viewModel =
             rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
 
-        OverlayShade(
-            modifier = modifier,
-            onScrimClicked = viewModel::onScrimClicked,
-        ) {
+        OverlayShade(modifier = modifier, onScrimClicked = viewModel::onScrimClicked) {
             Column {
                 ExpandedShadeHeader(
                     viewModelFactory = viewModel.shadeHeaderViewModelFactory,
@@ -98,40 +96,37 @@
                     modifier = Modifier.padding(QuickSettingsShade.Dimensions.Padding),
                 )
 
-                ShadeBody(
-                    viewModel = viewModel.quickSettingsContainerViewModel,
-                )
+                ShadeBody(viewModel = viewModel.quickSettingsContainerViewModel)
             }
         }
     }
 }
 
 @Composable
-fun ShadeBody(
-    viewModel: QuickSettingsContainerViewModel,
-) {
+fun SceneScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) {
     val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
 
     AnimatedContent(
         targetState = isEditing,
-        transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) }
+        transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) },
     ) { editing ->
         if (editing) {
             EditMode(
                 viewModel = viewModel.editModeViewModel,
-                modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
+                modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
             )
         } else {
             QuickSettingsLayout(
                 viewModel = viewModel,
-                modifier = Modifier.sysuiResTag("quick_settings_panel")
+                modifier = Modifier.sysuiResTag("quick_settings_panel"),
             )
         }
     }
 }
 
+/** Column containing Brightness and QS tiles. */
 @Composable
-private fun QuickSettingsLayout(
+fun SceneScope.QuickSettingsLayout(
     viewModel: QuickSettingsContainerViewModel,
     modifier: Modifier = Modifier,
 ) {
@@ -143,15 +138,18 @@
         BrightnessSliderContainer(
             viewModel = viewModel.brightnessSliderViewModel,
             modifier =
-                Modifier.fillMaxWidth()
-                    .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+                Modifier.fillMaxWidth().height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
         )
-        TileGrid(
-            viewModel = viewModel.tileGridViewModel,
-            modifier =
-                Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
-            viewModel.editModeViewModel::startEditing,
-        )
+        Box {
+            GridAnchor()
+            TileGrid(
+                viewModel = viewModel.tileGridViewModel,
+                modifier =
+                    Modifier.fillMaxWidth()
+                        .heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
+                viewModel.editModeViewModel::startEditing,
+            )
+        }
     }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index df101c5..dc9e267 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -126,17 +126,18 @@
                     awaitFirstDown(false)
                     viewModel.onSceneContainerUserInputStarted()
                 }
-            },
+            }
     ) {
         SceneTransitionLayout(
             state = state,
             modifier = modifier.fillMaxSize(),
             swipeSourceDetector = viewModel.edgeDetector,
+            gestureFilter = viewModel::shouldFilterGesture,
         ) {
             sceneByKey.forEach { (sceneKey, scene) ->
                 scene(
                     key = sceneKey,
-                    userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap())
+                    userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap()),
                 ) {
                     // Activate the scene.
                     LaunchedEffect(scene) { scene.activate() }
@@ -144,7 +145,7 @@
                     // Render the scene.
                     with(scene) {
                         this@scene.Content(
-                            modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
+                            modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize()
                         )
                     }
                 }
@@ -152,7 +153,7 @@
             overlayByKey.forEach { (overlayKey, overlay) ->
                 overlay(
                     key = overlayKey,
-                    userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap())
+                    userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap()),
                 ) {
                     // Activate the overlay.
                     LaunchedEffect(overlay) { overlay.activate() }
@@ -164,12 +165,7 @@
         }
 
         BottomRightCornerRibbon(
-            content = {
-                Text(
-                    text = "flexi\uD83E\uDD43",
-                    color = Color.White,
-                )
-            },
+            content = { Text(text = "flexi\uD83E\uDD43", color = Color.White) },
             modifier = Modifier.align(Alignment.BottomEnd),
         )
     }
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 f64d0ed..303a6f0 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
@@ -77,19 +77,40 @@
     }
     from(Scenes.Lockscreen, to = Scenes.QuickSettings) { lockscreenToQuickSettingsTransition() }
     from(Scenes.Lockscreen, to = Scenes.Gone) { lockscreenToGoneTransition() }
+    from(Scenes.QuickSettings, to = Scenes.Shade) {
+        reversed { shadeToQuickSettingsTransition() }
+        sharedElement(Notifications.Elements.HeadsUpNotificationPlaceholder, enabled = false)
+    }
     from(Scenes.Shade, to = Scenes.QuickSettings) { shadeToQuickSettingsTransition() }
+    from(Scenes.Shade, to = Scenes.Lockscreen) {
+        reversed { lockscreenToShadeTransition() }
+        sharedElement(Notifications.Elements.NotificationStackPlaceholder, enabled = false)
+    }
 
     // Overlay transitions
 
     to(Overlays.NotificationsShade) { toNotificationsShadeTransition() }
     to(Overlays.QuickSettingsShade) { toQuickSettingsShadeTransition() }
-    from(Overlays.NotificationsShade, Overlays.QuickSettingsShade) {
+    from(Overlays.NotificationsShade, to = Overlays.QuickSettingsShade) {
         notificationsShadeToQuickSettingsShadeTransition()
     }
+    from(Scenes.Gone, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
+        toNotificationsShadeTransition(durationScale = 0.9)
+    }
+    from(Scenes.Gone, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
+        toQuickSettingsShadeTransition(durationScale = 0.9)
+    }
+    from(Scenes.Lockscreen, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
+        toNotificationsShadeTransition(durationScale = 0.9)
+    }
+    from(Scenes.Lockscreen, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
+        toQuickSettingsShadeTransition(durationScale = 0.9)
+    }
 
     // Scene overscroll
 
     overscrollDisabled(Scenes.Gone, Orientation.Vertical)
+    overscrollDisabled(Scenes.Lockscreen, Orientation.Vertical)
     overscroll(Scenes.Bouncer, Orientation.Vertical) {
         translate(Bouncer.Elements.Content, y = { absoluteDistance })
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
index ac54896..4c0efd2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToBouncerTransition.kt
@@ -4,13 +4,19 @@
 import androidx.compose.animation.core.tween
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.TransitionBuilder
+import com.android.compose.animation.scene.UserActionDistance
 import com.android.systemui.bouncer.ui.composable.Bouncer
 
 const val FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION = 0.5f
+const val FROM_LOCK_SCREEN_TO_BOUNCER_SWIPE_DISTANCE_FRACTION = 0.5f
 
 fun TransitionBuilder.lockscreenToBouncerTransition() {
     spec = tween(durationMillis = 500)
 
+    distance = UserActionDistance { fromSceneSize, _ ->
+        fromSceneSize.height * FROM_LOCK_SCREEN_TO_BOUNCER_SWIPE_DISTANCE_FRACTION
+    }
+
     translate(Bouncer.Elements.Content, y = 300.dp)
     fractionRange(end = FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION) {
         fade(Bouncer.Elements.Background)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 6c4edf4..4162891 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -152,14 +152,17 @@
 /** Creates a union of [paddingValues] by using the max padding of each edge. */
 @Composable
 private fun combinePaddings(vararg paddingValues: PaddingValues): PaddingValues {
-    val layoutDirection = LocalLayoutDirection.current
-
-    return PaddingValues(
-        start = paddingValues.maxOfOrNull { it.calculateStartPadding(layoutDirection) } ?: 0.dp,
-        top = paddingValues.maxOfOrNull { it.calculateTopPadding() } ?: 0.dp,
-        end = paddingValues.maxOfOrNull { it.calculateEndPadding(layoutDirection) } ?: 0.dp,
-        bottom = paddingValues.maxOfOrNull { it.calculateBottomPadding() } ?: 0.dp,
-    )
+    return if (paddingValues.isEmpty()) {
+        PaddingValues(0.dp)
+    } else {
+        val layoutDirection = LocalLayoutDirection.current
+        PaddingValues(
+            start = paddingValues.maxOf { it.calculateStartPadding(layoutDirection) },
+            top = paddingValues.maxOf { it.calculateTopPadding() },
+            end = paddingValues.maxOf { it.calculateEndPadding(layoutDirection) },
+            bottom = paddingValues.maxOf { it.calculateBottomPadding() },
+        )
+    }
 }
 
 object OverlayShade {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 8a59e20..db0fe3e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -43,7 +43,6 @@
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
@@ -78,7 +77,6 @@
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.modifiers.padding
 import com.android.compose.modifiers.thenIf
-import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
 import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
@@ -89,6 +87,7 @@
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.controls.ui.composable.MediaContentPicker
+import com.android.systemui.media.controls.ui.composable.isLandscape
 import com.android.systemui.media.controls.ui.composable.shouldElevateMedia
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
@@ -197,6 +196,8 @@
             qsMediaHost = qsMediaHost,
             modifier = modifier,
             shadeSession = shadeSession,
+            usingCollapsedLandscapeMedia =
+                Utils.useCollapsedMediaInLandscape(LocalContext.current.resources),
         )
 
     init {
@@ -223,6 +224,7 @@
     qsMediaHost: MediaHost,
     modifier: Modifier = Modifier,
     shadeSession: SaveableSession,
+    usingCollapsedLandscapeMedia: Boolean,
 ) {
     val view = LocalView.current
     LaunchedEffect(Unit) {
@@ -245,6 +247,7 @@
                 mediaHost = qqsMediaHost,
                 modifier = modifier,
                 shadeSession = shadeSession,
+                usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
             )
         is ShadeMode.Split ->
             SplitShade(
@@ -275,14 +278,11 @@
     mediaHost: MediaHost,
     modifier: Modifier = Modifier,
     shadeSession: SaveableSession,
+    usingCollapsedLandscapeMedia: Boolean,
 ) {
     val cutoutLocation = LocalDisplayCutout.current.location
     val cutoutInsets = WindowInsets.Companion.displayCutout
-    val isLandscape = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
-    val usingCollapsedLandscapeMedia =
-        Utils.useCollapsedMediaInLandscape(LocalContext.current.resources)
-    val isExpanded = !usingCollapsedLandscapeMedia || !isLandscape
-    mediaHost.expansion = if (isExpanded) EXPANDED else COLLAPSED
+    mediaHost.expansion = if (usingCollapsedLandscapeMedia && isLandscape()) COLLAPSED else EXPANDED
 
     var maxNotifScrimTop by remember { mutableIntStateOf(0) }
     val tileSquishiness by
@@ -296,9 +296,9 @@
 
     val shouldPunchHoleBehindScrim =
         layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
-            layoutState.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
+            layoutState.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Shade)
     // Media is visible and we are in landscape on a small height screen
-    val mediaInRow = isMediaVisible && isLandscape
+    val mediaInRow = isMediaVisible && isLandscape()
     val mediaOffset by
         animateSceneDpAsState(value = InQQS, key = MediaLandscapeTopOffset, canOverflow = false)
 
@@ -380,6 +380,7 @@
                     mediaOffsetProvider = mediaOffsetProvider,
                     carouselController = mediaCarouselController,
                     modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Media),
+                    usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
                 )
 
                 NotificationScrollingStack(
@@ -636,6 +637,7 @@
     carouselController: MediaCarouselController,
     mediaOffsetProvider: ShadeMediaOffsetProvider,
     modifier: Modifier = Modifier,
+    usingCollapsedLandscapeMedia: Boolean = false,
 ) {
     MediaCarousel(
         modifier = modifier.fillMaxWidth(),
@@ -648,5 +650,6 @@
             } else {
                 { mediaOffsetProvider.offset }
             },
+        usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
     )
 }
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 f38a310..9891025 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
@@ -123,6 +123,10 @@
         overSlop: Float,
         pointersDown: Int,
     ): DragController {
+        if (startedPosition != null && layoutImpl.gestureFilter(startedPosition)) {
+            return NoOpDragController
+        }
+
         if (overSlop == 0f) {
             val oldDragController = dragController
             check(oldDragController != null && oldDragController.isDrivingTransition) {
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 cec8883..6e89814 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
@@ -47,6 +47,9 @@
  * @param state the state of this layout.
  * @param swipeSourceDetector the edge detector used to detect which edge a swipe is started from,
  *   if any.
+ * @param gestureFilter decides whether a drag gesture that started at the given start position
+ *   should be filtered. If the lambda returns `true`, the drag gesture will be ignored. If it
+ *   returns `false`, the drag gesture will be handled.
  * @param transitionInterceptionThreshold used during a scene transition. For the scene to be
  *   intercepted, the progress value must be above the threshold, and below (1 - threshold).
  * @param builder the configuration of the different scenes and overlays of this layout.
@@ -57,6 +60,7 @@
     modifier: Modifier = Modifier,
     swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector,
     swipeDetector: SwipeDetector = DefaultSwipeDetector,
+    gestureFilter: (startedPosition: Offset) -> Boolean = DefaultGestureFilter,
     @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0.05f,
     builder: SceneTransitionLayoutScope.() -> Unit,
 ) {
@@ -65,6 +69,7 @@
         modifier,
         swipeSourceDetector,
         swipeDetector,
+        gestureFilter,
         transitionInterceptionThreshold,
         onLayoutImpl = null,
         builder,
@@ -616,6 +621,7 @@
     modifier: Modifier = Modifier,
     swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector,
     swipeDetector: SwipeDetector = DefaultSwipeDetector,
+    gestureFilter: (startedPosition: Offset) -> Boolean = DefaultGestureFilter,
     transitionInterceptionThreshold: Float = 0f,
     onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null,
     builder: SceneTransitionLayoutScope.() -> Unit,
@@ -632,6 +638,7 @@
                 transitionInterceptionThreshold = transitionInterceptionThreshold,
                 builder = builder,
                 animationScope = animationScope,
+                gestureFilter = gestureFilter,
             )
             .also { onLayoutImpl?.invoke(it) }
     }
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 65c4043..9e7be37 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
@@ -31,6 +31,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.ApproachLayoutModifierNode
 import androidx.compose.ui.layout.ApproachMeasureScope
 import androidx.compose.ui.layout.LookaheadScope
@@ -70,6 +71,7 @@
      * animations.
      */
     internal val animationScope: CoroutineScope,
+    internal val gestureFilter: (startedPosition: Offset) -> Boolean,
 ) {
     /**
      * The map of [Scene]s.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt
index 54ee783..f758102 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt
@@ -17,6 +17,7 @@
 package com.android.compose.animation.scene
 
 import androidx.compose.runtime.Stable
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.pointer.PointerInputChange
 
 /** {@link SwipeDetector} helps determine whether a swipe gestured has occurred. */
@@ -31,6 +32,8 @@
 
 val DefaultSwipeDetector = PassthroughSwipeDetector()
 
+val DefaultGestureFilter = { _: Offset -> false }
+
 /** An {@link SwipeDetector} implementation that recognizes a swipe on any input. */
 class PassthroughSwipeDetector : SwipeDetector {
     override fun detectSwipe(change: PointerInputChange): Boolean {
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 fca92ca..b64b8be 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
@@ -108,6 +108,8 @@
 
         val transitionInterceptionThreshold = 0.05f
 
+        var gestureFilter: (startedPosition: Offset) -> Boolean = DefaultGestureFilter
+
         private val layoutImpl =
             SceneTransitionLayoutImpl(
                     state = layoutState,
@@ -120,6 +122,7 @@
                     // Use testScope and not backgroundScope here because backgroundScope does not
                     // work well with advanceUntilIdle(), which is used by some tests.
                     animationScope = testScope,
+                    gestureFilter = { startedPosition -> gestureFilter.invoke(startedPosition) },
                 )
                 .apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) }
 
@@ -317,6 +320,13 @@
     }
 
     @Test
+    fun onDragStarted_doesNotStartTransition_whenGestureFiltered() = runGestureTest {
+        gestureFilter = { _ -> true }
+        onDragStarted(overSlop = down(fractionOfScreen = 0.1f), expectedConsumedOverSlop = 0f)
+        assertIdle(currentScene = SceneA)
+    }
+
+    @Test
     fun afterSceneTransitionIsStarted_interceptDragEvents() = runGestureTest {
         val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f))
         assertTransition(currentScene = SceneA)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 312e62d..94d3b2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -62,7 +62,9 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.user.data.source.UserRecord;
+import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -96,6 +98,7 @@
     private FalsingA11yDelegate mFalsingA11yDelegate;
 
     private KeyguardSecurityContainer mKeyguardSecurityContainer;
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Before
     public void setup() {
@@ -106,6 +109,7 @@
         mSecurityViewFlipper = new KeyguardSecurityViewFlipper(getContext());
         mSecurityViewFlipper.setId(View.generateViewId());
         mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+        mKeyguardSecurityContainer.setBackgroundExecutor(mExecutor);
         mKeyguardSecurityContainer.setRight(VIEW_WIDTH);
         mKeyguardSecurityContainer.setLeft(0);
         mKeyguardSecurityContainer.setTop(0);
@@ -342,7 +346,7 @@
 
     @Test
     public void testTwoOrMoreUsersDoesAllowDropDown() {
-        // GIVEN one user has been setup
+        // GIVEN two users have been setup
         ArrayList<UserRecord> records = buildUserRecords(2);
         when(mUserSwitcherController.getCurrentUserRecord()).thenReturn(records.get(0));
         when(mUserSwitcherController.getUsers()).thenReturn(records);
@@ -350,7 +354,7 @@
         // WHEN UserSwitcherViewMode is initialized
         setupUserSwitcher();
 
-        // THEN the UserSwitcher anchor should not be clickable
+        // THEN the UserSwitcher anchor should be clickable
         ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
         assertThat(anchor.isClickable()).isTrue();
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSliceViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSliceViewTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardStatusViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/NumPadAnimatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/NumPadAnimatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/PinShapeHintingViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/PinShapeHintingViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TestScopeProvider.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/TestScopeProvider.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/TestScopeProvider.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/TestScopeProvider.kt
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index cbad133..8c7cd61 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -114,14 +114,15 @@
 
     private val backActionInteractor: BackActionInteractor by lazy {
         BackActionInteractor(
-                testScope.backgroundScope,
-                statusBarStateController,
-                statusBarKeyguardViewManager,
-                shadeController,
-                notificationShadeWindowController,
-                windowRootViewVisibilityInteractor
-            )
-            .apply { this.setup(qsController, shadeBackActionInteractor) }
+            testScope.backgroundScope,
+            statusBarStateController,
+            statusBarKeyguardViewManager,
+            shadeController,
+            notificationShadeWindowController,
+            windowRootViewVisibilityInteractor,
+            shadeBackActionInteractor,
+            qsController,
+        )
     }
 
     private val powerInteractor = PowerInteractorFactory.create().powerInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 4bc71fd..75a77cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -27,17 +27,6 @@
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.fingerprint.FingerprintSensorProperties
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import com.android.keyguard.keyguardUpdateMonitor
-import com.android.systemui.SysuiTestableContext
-import com.android.systemui.biometrics.data.repository.biometricStatusRepository
-import com.android.systemui.biometrics.shared.model.AuthenticationReason
-import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.res.R
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
 
 /** Create [FingerprintSensorPropertiesInternal] for a test. */
 internal fun fingerprintSensorPropertiesInternal(
@@ -156,67 +145,3 @@
     info.negativeButtonText = negativeButton
     return info
 }
-
-@OptIn(ExperimentalCoroutinesApi::class)
-internal fun TestScope.updateSfpsIndicatorRequests(
-    kosmos: Kosmos,
-    mContext: SysuiTestableContext,
-    primaryBouncerRequest: Boolean? = null,
-    alternateBouncerRequest: Boolean? = null,
-    biometricPromptRequest: Boolean? = null,
-    // TODO(b/365182034): update when rest to unlock feature is implemented
-    //    progressBarShowing: Boolean? = null
-) {
-    biometricPromptRequest?.let { hasBiometricPromptRequest ->
-        if (hasBiometricPromptRequest) {
-            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
-                AuthenticationReason.BiometricPromptAuthentication
-            )
-        } else {
-            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
-                AuthenticationReason.NotRunning
-            )
-        }
-    }
-
-    primaryBouncerRequest?.let { hasPrimaryBouncerRequest ->
-        updatePrimaryBouncer(
-            kosmos,
-            mContext,
-            isShowing = hasPrimaryBouncerRequest,
-            isAnimatingAway = false,
-            fpsDetectionRunning = true,
-            isUnlockingWithFpAllowed = true
-        )
-    }
-
-    alternateBouncerRequest?.let { hasAlternateBouncerRequest ->
-        kosmos.keyguardBouncerRepository.setAlternateVisible(hasAlternateBouncerRequest)
-    }
-
-    // TODO(b/365182034): set progress bar visibility when rest to unlock feature is implemented
-
-    runCurrent()
-}
-
-internal fun updatePrimaryBouncer(
-    kosmos: Kosmos,
-    mContext: SysuiTestableContext,
-    isShowing: Boolean,
-    isAnimatingAway: Boolean,
-    fpsDetectionRunning: Boolean,
-    isUnlockingWithFpAllowed: Boolean,
-) {
-    kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
-    kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
-    val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
-    kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
-        primaryStartDisappearAnimation
-    )
-
-    whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
-        .thenReturn(fpsDetectionRunning)
-    whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
-        .thenReturn(isUnlockingWithFpAllowed)
-    mContext.orCreateTestableResources.addOverride(R.bool.config_show_sidefps_hint_on_bouncer, true)
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index d86890b..437a4b3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics;
 
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
@@ -1437,4 +1438,38 @@
         // THEN vibrate is used
         verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
     }
+
+    @Test
+    public void onAcquiredCalbacks() {
+        runWithAllParams(
+                this::ultrasonicCallbackOnAcquired);
+    }
+
+    public void ultrasonicCallbackOnAcquired(TestParams testParams) throws RemoteException{
+        if (testParams.sensorProps.sensorType
+                == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC) {
+            reset(mUdfpsView);
+
+            UdfpsController.Callback callbackMock = mock(UdfpsController.Callback.class);
+            mUdfpsController.addCallback(callbackMock);
+
+            // GIVEN UDFPS overlay is showing
+            mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+                    BiometricRequestConstants.REASON_AUTH_KEYGUARD,
+                    mUdfpsOverlayControllerCallback);
+            mFgExecutor.runAllReady();
+
+            verify(mFingerprintManager).setUdfpsOverlayController(
+                    mUdfpsOverlayControllerCaptor.capture());
+            mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_START);
+            mFgExecutor.runAllReady();
+
+            verify(callbackMock).onFingerDown();
+
+            mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD);
+            mFgExecutor.runAllReady();
+
+            verify(callbackMock).onFingerUp();
+        }
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
deleted file mode 100644
index 298b54a..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
+++ /dev/null
@@ -1,174 +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.biometrics.domain.interactor
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.biometricStatusRepository
-import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
-import com.android.systemui.biometrics.shared.model.AuthenticationReason
-import com.android.systemui.biometrics.shared.model.FingerprintSensorType
-import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.biometrics.updateSfpsIndicatorRequests
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.display.data.repository.displayRepository
-import com.android.systemui.display.data.repository.displayStateRepository
-import com.android.systemui.kosmos.testScope
-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.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class SideFpsOverlayInteractorTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val underTest = kosmos.sideFpsOverlayInteractor
-
-    @Test
-    fun verifyIsShowingFalse_whenInRearDisplayMode() {
-        kosmos.testScope.runTest {
-            val isShowing by collectLastValue(underTest.isShowing)
-            setupTestConfiguration(isInRearDisplayMode = true)
-
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
-            runCurrent()
-
-            assertThat(isShowing).isFalse()
-        }
-    }
-
-    @Test
-    fun verifyIsShowingUpdates_onPrimaryBouncerShowAndHide() {
-        kosmos.testScope.runTest {
-            val isShowing by collectLastValue(underTest.isShowing)
-            setupTestConfiguration(isInRearDisplayMode = false)
-
-            // Show primary bouncer
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
-            runCurrent()
-
-            assertThat(isShowing).isTrue()
-
-            // Hide primary bouncer
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false)
-            runCurrent()
-
-            assertThat(isShowing).isFalse()
-        }
-    }
-
-    @Test
-    fun verifyIsShowingUpdates_onAlternateBouncerShowAndHide() {
-        kosmos.testScope.runTest {
-            val isShowing by collectLastValue(underTest.isShowing)
-            setupTestConfiguration(isInRearDisplayMode = false)
-
-            updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true)
-            runCurrent()
-
-            assertThat(isShowing).isTrue()
-
-            // Hide alternate bouncer
-            updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
-            runCurrent()
-
-            assertThat(isShowing).isFalse()
-        }
-    }
-
-    @Test
-    fun verifyIsShowingUpdates_onSystemServerAuthenticationStartedAndStopped() {
-        kosmos.testScope.runTest {
-            val isShowing by collectLastValue(underTest.isShowing)
-            setupTestConfiguration(isInRearDisplayMode = false)
-
-            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
-            runCurrent()
-
-            assertThat(isShowing).isTrue()
-
-            // System server authentication stopped
-            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false)
-            runCurrent()
-
-            assertThat(isShowing).isFalse()
-        }
-    }
-
-    // On progress bar shown - hide indicator
-    // On progress bar hidden - show indicator
-    // TODO(b/365182034): update + enable when rest to unlock feature is implemented
-    @Ignore("b/365182034")
-    @Test
-    fun verifyIsShowingUpdates_onProgressBarInteraction() {
-        kosmos.testScope.runTest {
-            val isShowing by collectLastValue(underTest.isShowing)
-            setupTestConfiguration(isInRearDisplayMode = false)
-
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
-            runCurrent()
-
-            assertThat(isShowing).isTrue()
-
-            //            updateSfpsIndicatorRequests(
-            //                kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
-            // true
-            //            )
-            runCurrent()
-
-            assertThat(isShowing).isFalse()
-
-            // Set progress bar invisible
-            //            updateSfpsIndicatorRequests(
-            //                kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
-            // false
-            //            )
-            runCurrent()
-
-            // Verify indicator shown
-            assertThat(isShowing).isTrue()
-        }
-    }
-
-    private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) {
-        kosmos.fingerprintPropertyRepository.setProperties(
-            sensorId = 1,
-            strength = SensorStrength.STRONG,
-            sensorType = FingerprintSensorType.POWER_BUTTON,
-            sensorLocations = emptyMap()
-        )
-
-        kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
-        kosmos.displayRepository.emitDisplayChangeEvent(0)
-        runCurrent()
-
-        kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
-            AuthenticationReason.NotRunning
-        )
-        // TODO(b/365182034): set progress bar visibility once rest to unlock feature is implemented
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 2eea668..7fa165c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -16,48 +16,64 @@
 
 package com.android.systemui.biometrics.ui.binder
 
+import android.animation.Animator
+import android.graphics.Rect
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayManagerGlobal
 import android.testing.TestableLooper
+import android.view.Display
+import android.view.DisplayInfo
 import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
 import android.view.WindowManager
+import android.view.WindowMetrics
 import android.view.layoutInflater
 import android.view.windowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.data.repository.biometricStatusRepository
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
 import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.biometrics.updateSfpsIndicatorRequests
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.display.data.repository.displayStateRepository
+import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito
 import org.mockito.Mockito.any
 import org.mockito.Mockito.inOrder
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.firstValue
+import org.mockito.kotlin.argumentCaptor
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -67,25 +83,84 @@
     private val kosmos = testKosmos()
 
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+    @Mock private lateinit var displayManager: DisplayManager
+    @Mock
+    private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
     @Mock private lateinit var layoutInflater: LayoutInflater
     @Mock private lateinit var sideFpsView: View
-    @Captor private lateinit var viewCaptor: ArgumentCaptor<View>
+
+    private val contextDisplayInfo = DisplayInfo()
+
+    private var displayWidth: Int = 0
+    private var displayHeight: Int = 0
+    private var boundsWidth: Int = 0
+    private var boundsHeight: Int = 0
+
+    private lateinit var deviceConfig: DeviceConfig
+    private lateinit var sensorLocation: SensorLocationInternal
+
+    enum class DeviceConfig {
+        X_ALIGNED,
+        Y_ALIGNED,
+    }
 
     @Before
     fun setup() {
         allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
+
+        mContext = spy(mContext)
+
+        val resources = mContext.resources
+        whenever(mContext.display)
+            .thenReturn(
+                Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
+            )
+
         kosmos.layoutInflater = layoutInflater
+
+        whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
+            .thenReturn(MutableStateFlow(false))
+
+        context.addMockSystemService(DisplayManager::class.java, displayManager)
         context.addMockSystemService(WindowManager::class.java, kosmos.windowManager)
+
         `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sideFpsView)
         `when`(sideFpsView.requireViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
             .thenReturn(mock(LottieAnimationView::class.java))
+        with(mock(ViewPropertyAnimator::class.java)) {
+            `when`(sideFpsView.animate()).thenReturn(this)
+            `when`(alpha(Mockito.anyFloat())).thenReturn(this)
+            `when`(setStartDelay(Mockito.anyLong())).thenReturn(this)
+            `when`(setDuration(Mockito.anyLong())).thenReturn(this)
+            `when`(setListener(any())).thenAnswer {
+                (it.arguments[0] as Animator.AnimatorListener).onAnimationEnd(
+                    mock(Animator::class.java)
+                )
+                this
+            }
+        }
     }
 
     @Test
     fun verifyIndicatorNotAdded_whenInRearDisplayMode() {
         kosmos.testScope.runTest {
-            setupTestConfiguration(isInRearDisplayMode = true)
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = true
+            )
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
             verify(kosmos.windowManager, never()).addView(any(), any())
         }
     }
@@ -93,14 +168,33 @@
     @Test
     fun verifyIndicatorShowAndHide_onPrimaryBouncerShowAndHide() {
         kosmos.testScope.runTest {
-            setupTestConfiguration(isInRearDisplayMode = false)
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+            // Show primary bouncer
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
             runCurrent()
 
             verify(kosmos.windowManager).addView(any(), any())
 
             // Hide primary bouncer
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = false)
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
             runCurrent()
 
             verify(kosmos.windowManager).removeView(any())
@@ -110,19 +204,30 @@
     @Test
     fun verifyIndicatorShowAndHide_onAlternateBouncerShowAndHide() {
         kosmos.testScope.runTest {
-            setupTestConfiguration(isInRearDisplayMode = false)
-            updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = true)
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+            // Show alternate bouncer
+            kosmos.keyguardBouncerRepository.setAlternateVisible(true)
             runCurrent()
 
             verify(kosmos.windowManager).addView(any(), any())
 
+            var viewCaptor = argumentCaptor<View>()
             verify(kosmos.windowManager).addView(viewCaptor.capture(), any())
             verify(viewCaptor.firstValue)
                 .announceForAccessibility(
                     mContext.getText(R.string.accessibility_side_fingerprint_indicator_label)
                 )
 
-            updateSfpsIndicatorRequests(kosmos, mContext, alternateBouncerRequest = false)
+            // Hide alternate bouncer
+            kosmos.keyguardBouncerRepository.setAlternateVisible(false)
             runCurrent()
 
             verify(kosmos.windowManager).removeView(any())
@@ -132,14 +237,30 @@
     @Test
     fun verifyIndicatorShownAndHidden_onSystemServerAuthenticationStartedAndStopped() {
         kosmos.testScope.runTest {
-            setupTestConfiguration(isInRearDisplayMode = false)
-            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            // System server authentication started
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
             runCurrent()
 
             verify(kosmos.windowManager).addView(any(), any())
 
             // System server authentication stopped
-            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = false)
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
             runCurrent()
 
             verify(kosmos.windowManager).removeView(any())
@@ -148,35 +269,45 @@
 
     // On progress bar shown - hide indicator
     // On progress bar hidden - show indicator
-    // TODO(b/365182034): update + enable when rest to unlock feature is implemented
-    @Ignore("b/365182034")
     @Test
     fun verifyIndicatorProgressBarInteraction() {
         kosmos.testScope.runTest {
             // Pre-auth conditions
-            setupTestConfiguration(isInRearDisplayMode = false)
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+            // Show primary bouncer
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
             runCurrent()
 
             val inOrder = inOrder(kosmos.windowManager)
+
             // Verify indicator shown
             inOrder.verify(kosmos.windowManager).addView(any(), any())
 
             // Set progress bar visible
-            //            updateSfpsIndicatorRequests(
-            //                kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
-            // true
-            //            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(true)
+
             runCurrent()
 
             // Verify indicator hidden
             inOrder.verify(kosmos.windowManager).removeView(any())
 
             // Set progress bar invisible
-            //            updateSfpsIndicatorRequests(
-            //                kosmos, mContext, primaryBouncerRequest = true, progressBarShowing =
-            // false
-            //            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
             runCurrent()
 
             // Verify indicator shown
@@ -184,18 +315,78 @@
         }
     }
 
-    private suspend fun TestScope.setupTestConfiguration(isInRearDisplayMode: Boolean) {
+    private fun updatePrimaryBouncer(
+        isShowing: Boolean,
+        isAnimatingAway: Boolean,
+        fpsDetectionRunning: Boolean,
+        isUnlockingWithFpAllowed: Boolean,
+    ) {
+        kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
+        kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
+        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+        kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
+            primaryStartDisappearAnimation
+        )
+
+        whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
+            .thenReturn(fpsDetectionRunning)
+        whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+            .thenReturn(isUnlockingWithFpAllowed)
+        mContext.orCreateTestableResources.addOverride(
+            R.bool.config_show_sidefps_hint_on_bouncer,
+            true
+        )
+    }
+
+    private suspend fun TestScope.setupTestConfiguration(
+        deviceConfig: DeviceConfig,
+        rotation: DisplayRotation = DisplayRotation.ROTATION_0,
+        isInRearDisplayMode: Boolean,
+    ) {
+        this@SideFpsOverlayViewBinderTest.deviceConfig = deviceConfig
+
+        when (deviceConfig) {
+            DeviceConfig.X_ALIGNED -> {
+                displayWidth = 3000
+                displayHeight = 1500
+                boundsWidth = 200
+                boundsHeight = 100
+                sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
+            }
+            DeviceConfig.Y_ALIGNED -> {
+                displayWidth = 2500
+                displayHeight = 2000
+                boundsWidth = 100
+                boundsHeight = 200
+                sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
+            }
+        }
+
+        whenever(kosmos.windowManager.maximumWindowMetrics)
+            .thenReturn(
+                WindowMetrics(
+                    Rect(0, 0, displayWidth, displayHeight),
+                    mock(WindowInsets::class.java),
+                )
+            )
+
+        contextDisplayInfo.uniqueId = DISPLAY_ID
+
         kosmos.fingerprintPropertyRepository.setProperties(
             sensorId = 1,
             strength = SensorStrength.STRONG,
             sensorType = FingerprintSensorType.POWER_BUTTON,
-            sensorLocations = emptyMap()
+            sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
         )
 
         kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
-        kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+        kosmos.displayStateRepository.setCurrentRotation(rotation)
         kosmos.displayRepository.emitDisplayChangeEvent(0)
         kosmos.sideFpsOverlayViewBinder.start()
         runCurrent()
     }
+
+    companion object {
+        private const val DISPLAY_ID = "displayId"
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index 27b1371..0db7b62 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -30,19 +30,23 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.airbnb.lottie.model.KeyPath
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.settingslib.Utils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.data.repository.biometricStatusRepository
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
 import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.LottieCallback
 import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.biometrics.updateSfpsIndicatorRequests
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.display.data.repository.displayStateRepository
+import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
@@ -280,7 +284,17 @@
         kosmos.testScope.runTest {
             val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
 
-            updateSfpsIndicatorRequests(kosmos, mContext, primaryBouncerRequest = true)
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
             runCurrent()
 
             assertThat(lottieCallbacks)
@@ -298,7 +312,17 @@
             val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
             setDarkMode(true)
 
-            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
             runCurrent()
 
             assertThat(lottieCallbacks)
@@ -314,7 +338,17 @@
             val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
             setDarkMode(false)
 
-            updateSfpsIndicatorRequests(kosmos, mContext, biometricPromptRequest = true)
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
             runCurrent()
 
             assertThat(lottieCallbacks)
@@ -337,6 +371,29 @@
         mContext.resources.configuration.uiMode = uiMode
     }
 
+    private fun updatePrimaryBouncer(
+        isShowing: Boolean,
+        isAnimatingAway: Boolean,
+        fpsDetectionRunning: Boolean,
+        isUnlockingWithFpAllowed: Boolean,
+    ) {
+        kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
+        kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
+        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+        kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
+            primaryStartDisappearAnimation
+        )
+
+        whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
+            .thenReturn(fpsDetectionRunning)
+        whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+            .thenReturn(isUnlockingWithFpAllowed)
+        mContext.orCreateTestableResources.addOverride(
+            R.bool.config_show_sidefps_hint_on_bouncer,
+            true
+        )
+    }
+
     private suspend fun TestScope.setupTestConfiguration(
         deviceConfig: DeviceConfig,
         rotation: DisplayRotation = DisplayRotation.ROTATION_0,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 492543f..af3ddfc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -310,6 +310,41 @@
                 .isEqualTo(displayId)
         }
 
+    @Test
+    fun afterSuccessfulAuthentication_focusIsNotRequested() =
+        testScope.runTest {
+            val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
+            val textInputFocusRequested by collectLastValue(underTest.isTextFieldFocusRequested)
+            lockDeviceAndOpenPasswordBouncer()
+
+            // remove focus from text field
+            underTest.onTextFieldFocusChanged(false)
+            runCurrent()
+
+            // focus should be requested
+            assertThat(textInputFocusRequested).isTrue()
+
+            // simulate text field getting focus
+            underTest.onTextFieldFocusChanged(true)
+            runCurrent()
+
+            // focus should not be requested anymore
+            assertThat(textInputFocusRequested).isFalse()
+
+            // authenticate successfully.
+            underTest.onPasswordInputChanged("password")
+            underTest.onAuthenticateKeyPressed()
+            runCurrent()
+
+            assertThat(authResult).isTrue()
+
+            // remove focus from text field
+            underTest.onTextFieldFocusChanged(false)
+            runCurrent()
+            // focus should not be requested again
+            assertThat(textInputFocusRequested).isFalse()
+        }
+
     private fun TestScope.switchToScene(toScene: SceneKey) {
         val currentScene by collectLastValue(sceneInteractor.currentScene)
         val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
@@ -327,10 +362,7 @@
         switchToScene(Scenes.Bouncer)
     }
 
-    private suspend fun TestScope.setLockout(
-        isLockedOut: Boolean,
-        failedAttemptCount: Int = 5,
-    ) {
+    private suspend fun TestScope.setLockout(isLockedOut: Boolean, failedAttemptCount: Int = 5) {
         if (isLockedOut) {
             repeat(failedAttemptCount) {
                 kosmos.fakeAuthenticationRepository.reportAuthenticationAttempt(false)
@@ -350,7 +382,7 @@
         kosmos.fakeUserRepository.selectedUser.value =
             SelectedUserModel(
                 userInfo = userInfo,
-                selectionStatus = SelectionStatus.SELECTION_COMPLETE
+                selectionStatus = SelectionStatus.SELECTION_COMPLETE,
             )
         advanceTimeBy(PasswordBouncerViewModel.DELAY_TO_FETCH_IMES)
     }
@@ -374,7 +406,7 @@
             subtypes =
                 List(auxiliarySubtypes + nonAuxiliarySubtypes) {
                     InputMethodModel.Subtype(subtypeId = it, isAuxiliary = it < auxiliarySubtypes)
-                }
+                },
         )
     }
 
@@ -383,9 +415,6 @@
         private const val WRONG_PASSWORD = "Wrong password"
 
         private val USER_INFOS =
-            listOf(
-                UserInfo(100, "First user", 0),
-                UserInfo(101, "Second user", 0),
-            )
+            listOf(UserInfo(100, "First user", 0), UserInfo(101, "Second user", 0))
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
index 2ba4bf9..e25c1a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
@@ -34,8 +34,6 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.realKeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.DozeStateModel
-import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
@@ -50,8 +48,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
-import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.seconds
@@ -220,15 +216,11 @@
     @Test
     fun transition_from_hub_end_in_dream() =
         testScope.runTest {
-            // Device is dreaming and not dozing.
-            kosmos.powerInteractor.setAwakeForTest()
-            kosmos.fakeKeyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
-            )
+            // Device is dreaming and occluded.
             kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
             kosmos.fakeKeyguardRepository.setDreaming(true)
             kosmos.fakeKeyguardRepository.setDreamingWithOverlay(true)
-            advanceTimeBy(600L)
+            runCurrent()
 
             sceneTransitions.value = hubToBlank
 
@@ -663,7 +655,7 @@
                     from = LOCKSCREEN,
                     to = OCCLUDED,
                     animator = null,
-                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                    modeOnCanceled = TransitionModeOnCanceled.RESET,
                 )
             )
 
@@ -750,7 +742,7 @@
                     from = LOCKSCREEN,
                     to = OCCLUDED,
                     animator = null,
-                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                    modeOnCanceled = TransitionModeOnCanceled.RESET,
                 )
             )
 
@@ -852,8 +844,8 @@
                     to = ALTERNATE_BOUNCER,
                     animator = null,
                     ownerName = "external",
-                    modeOnCanceled = TransitionModeOnCanceled.RESET
-                ),
+                    modeOnCanceled = TransitionModeOnCanceled.RESET,
+                )
             )
 
             val allSteps by collectValues(keyguardTransitionRepository.transitions)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
index b3ffc71..d6734e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.usage.UsageEvents
 import android.content.pm.UserInfo
+import android.service.dream.dreamManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -28,6 +29,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.shared.system.taskStackChangeListeners
@@ -48,6 +50,7 @@
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
@@ -90,6 +93,27 @@
         }
 
     @Test
+    fun testNewTaskStartsWhileOnHub_stopsDream() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+            moveTaskToFront()
+
+            argumentCaptor<OnDismissAction>().apply {
+                verify(activityStarter).dismissKeyguardThenExecute(capture(), anyOrNull(), any())
+
+                firstValue.onDismiss()
+                runCurrent()
+
+                // Dream is stopped once keyguard is dismissed.
+                verify(kosmos.dreamManager).stopDream()
+            }
+        }
+
+    @Test
     fun testNewTaskStartsAfterExitingHub_doesNotTriggerUnlock() =
         testScope.runTest {
             transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
@@ -209,7 +233,7 @@
                     ownerName = "test",
                 ),
             ),
-            testScope
+            testScope,
         )
         runCurrent()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt
index 9da6885..52ed231 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartableTest.kt
@@ -19,11 +19,13 @@
 import android.content.Context
 import android.content.Intent
 import android.os.UserHandle
+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.broadcast.broadcastDispatcher
 import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
+import com.android.systemui.shared.Flags
 import com.android.systemui.testKosmos
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,16 +45,17 @@
         KeyboardTouchpadTutorialCoreStartable(
             { mock<TutorialNotificationCoordinator>() },
             broadcastDispatcher,
-            context
+            context,
         )
 
     @Test
+    @EnableFlags(Flags.FLAG_NEW_TOUCHPAD_GESTURES_TUTORIAL)
     fun registersBroadcastReceiverStartingActivityAsSystemUser() {
         underTest.start()
 
         broadcastDispatcher.sendIntentToMatchingReceiversOnly(
             context,
-            Intent("com.android.systemui.action.KEYBOARD_TOUCHPAD_TUTORIAL")
+            Intent("com.android.systemui.action.KEYBOARD_TOUCHPAD_TUTORIAL"),
         )
 
         verify(context).startActivityAsUser(any(), eq(UserHandle.SYSTEM))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index bd6cfff..93754fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.doze.DozeTransitionCallback
 import com.android.systemui.doze.DozeTransitionListener
 import com.android.systemui.dreams.DreamOverlayCallbackController
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.DozeStateModel
@@ -288,6 +289,7 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun dozeAmount() =
         testScope.runTest {
             val values = mutableListOf<Float>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index fac9312..ff0a4a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -26,8 +26,10 @@
 import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
+import com.android.systemui.communal.data.repository.communalSceneRepository
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -50,10 +52,12 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
+import com.google.common.truth.Truth
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -219,6 +223,28 @@
         }
 
     @Test
+    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_SCENE_CONTAINER)
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    fun testTransitionToGlanceableHub_onWakeup_ifAvailable() =
+        testScope.runTest {
+            // Hub is available.
+            whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
+            kosmos.setCommunalAvailable(true)
+            runCurrent()
+
+            // Device turns on.
+            powerInteractor.setAwakeForTest()
+            advanceTimeBy(50L)
+            runCurrent()
+
+            // We transition to the hub when waking up.
+            Truth.assertThat(kosmos.communalSceneRepository.currentScene.value)
+                .isEqualTo(CommunalScenes.Communal)
+            // No transitions are directly started by this interactor.
+            assertThat(transitionRepository).noTransitionsStarted()
+        }
+
+    @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() =
         testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 638c957..fa304c9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -16,12 +16,19 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
+import android.service.dream.dreamManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
+import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -36,6 +43,7 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -43,26 +51,52 @@
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class FromDreamingTransitionInteractorTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class FromDreamingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() {
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+                .andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     private val kosmos =
         testKosmos().apply {
             this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
         }
 
     private val testScope = kosmos.testScope
-    private val underTest = kosmos.fromDreamingTransitionInteractor
+    private val underTest by lazy { kosmos.fromDreamingTransitionInteractor }
 
     private val powerInteractor = kosmos.powerInteractor
     private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
 
     @Before
     fun setup() {
+        runBlocking {
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DREAMING,
+                testScope,
+            )
+            reset(transitionRepository)
+            kosmos.setCommunalAvailable(true)
+        }
         underTest.start()
     }
 
@@ -86,10 +120,7 @@
             runCurrent()
 
             assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.OCCLUDED,
-                )
+                .startedTransition(from = KeyguardState.DREAMING, to = KeyguardState.OCCLUDED)
         }
 
     @Test
@@ -126,7 +157,7 @@
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.DREAMING,
-                testScope
+                testScope,
             )
             kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockMode.NONE)
 
@@ -139,10 +170,7 @@
             advanceTimeBy(60L)
 
             assertThat(transitionRepository)
-                .startedTransition(
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.LOCKSCREEN,
-                )
+                .startedTransition(from = KeyguardState.DREAMING, to = KeyguardState.LOCKSCREEN)
         }
 
     @Test
@@ -164,4 +192,32 @@
                     to = KeyguardState.ALTERNATE_BOUNCER,
                 )
         }
+
+    @Test
+    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+    @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
+    fun testTransitionToGlanceableHubOnWake() =
+        testScope.runTest {
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DREAMING,
+                testScope,
+            )
+            reset(transitionRepository)
+
+            whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
+            kosmos.setCommunalAvailable(true)
+            runCurrent()
+
+            // Device wakes up.
+            powerInteractor.setAwakeForTest()
+            advanceTimeBy(150L)
+            runCurrent()
+
+            // We transition to the hub when waking up.
+            assertThat(kosmos.communalSceneRepository.currentScene.value)
+                .isEqualTo(CommunalScenes.Communal)
+            // No transitions are directly started by this interactor.
+            assertThat(transitionRepository).noTransitionsStarted()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 41cc953..29035ce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.scene.data.repository.Idle
 import com.android.systemui.scene.data.repository.Transition
 import com.android.systemui.scene.data.repository.setSceneTransition
-import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.testKosmos
@@ -76,11 +75,11 @@
                 transitionInteractor = kosmos.keyguardTransitionInteractor,
                 dismissInteractor = dismissInteractor,
                 applicationScope = testScope.backgroundScope,
-                sceneInteractor = { kosmos.sceneInteractor },
                 deviceUnlockedInteractor = { kosmos.deviceUnlockedInteractor },
                 powerInteractor = kosmos.powerInteractor,
                 alternateBouncerInteractor = kosmos.alternateBouncerInteractor,
                 shadeInteractor = { kosmos.shadeInteractor },
+                keyguardInteractor = { kosmos.keyguardInteractor },
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index b843fd5..3fb3eea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -29,15 +29,21 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeCommandQueue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.shared.model.CameraLaunchType
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -67,12 +73,11 @@
     private val testScope = kosmos.testScope
     private val repository by lazy { kosmos.fakeKeyguardRepository }
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
-    private val commandQueue by lazy { kosmos.fakeCommandQueue }
     private val configRepository by lazy { kosmos.fakeConfigurationRepository }
     private val bouncerRepository by lazy { kosmos.keyguardBouncerRepository }
     private val shadeRepository by lazy { kosmos.shadeRepository }
     private val powerInteractor by lazy { kosmos.powerInteractor }
+    private val keyguardRepository by lazy { kosmos.keyguardRepository }
     private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
 
     private val transitionState: MutableStateFlow<ObservableTransitionState> =
@@ -178,8 +183,8 @@
             assertThat(dismissAlpha).isEqualTo(1f)
 
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.AOD,
-                to = KeyguardState.LOCKSCREEN,
+                from = AOD,
+                to = LOCKSCREEN,
                 testScope,
             )
 
@@ -204,8 +209,8 @@
             assertThat(dismissAlpha.size).isEqualTo(1)
 
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.AOD,
-                to = KeyguardState.LOCKSCREEN,
+                from = AOD,
+                to = LOCKSCREEN,
                 testScope,
             )
 
@@ -266,13 +271,13 @@
             keyguardTransitionRepository.sendTransitionSteps(
                 listOf(
                     TransitionStep(
-                        from = KeyguardState.AOD,
+                        from = AOD,
                         to = KeyguardState.GONE,
                         value = 0f,
-                        transitionState = TransitionState.STARTED,
+                        transitionState = STARTED,
                     ),
                     TransitionStep(
-                        from = KeyguardState.AOD,
+                        from = AOD,
                         to = KeyguardState.GONE,
                         value = 0.1f,
                         transitionState = TransitionState.RUNNING,
@@ -302,7 +307,7 @@
             shadeRepository.setLegacyShadeExpansion(0f)
 
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.AOD,
+                from = AOD,
                 to = KeyguardState.GONE,
                 testScope,
             )
@@ -324,8 +329,8 @@
             shadeRepository.setLegacyShadeExpansion(0f)
 
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.AOD,
-                to = KeyguardState.LOCKSCREEN,
+                from = AOD,
+                to = LOCKSCREEN,
                 testScope,
             )
 
@@ -346,8 +351,8 @@
             shadeRepository.setLegacyShadeExpansion(1f)
 
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.AOD,
-                to = KeyguardState.LOCKSCREEN,
+                from = AOD,
+                to = LOCKSCREEN,
                 testScope,
             )
 
@@ -370,13 +375,13 @@
             keyguardTransitionRepository.sendTransitionSteps(
                 listOf(
                     TransitionStep(
-                        from = KeyguardState.AOD,
+                        from = AOD,
                         to = KeyguardState.GONE,
                         value = 0f,
-                        transitionState = TransitionState.STARTED,
+                        transitionState = STARTED,
                     ),
                     TransitionStep(
-                        from = KeyguardState.AOD,
+                        from = AOD,
                         to = KeyguardState.GONE,
                         value = 0.1f,
                         transitionState = TransitionState.RUNNING,
@@ -468,4 +473,63 @@
             runCurrent()
             assertThat(isAnimate).isFalse()
         }
+
+    @Test
+    @EnableSceneContainer
+    fun dozeAmount_updatedByAodTransitionWhenAodEnabled() =
+        testScope.runTest {
+            val dozeAmount by collectLastValue(underTest.dozeAmount)
+
+            keyguardRepository.setAodAvailable(true)
+
+            sendTransitionStep(TransitionStep(to = AOD, value = 0f, transitionState = STARTED))
+            assertThat(dozeAmount).isEqualTo(0f)
+
+            sendTransitionStep(TransitionStep(to = AOD, value = 0.5f, transitionState = RUNNING))
+            assertThat(dozeAmount).isEqualTo(0.5f)
+
+            sendTransitionStep(TransitionStep(to = AOD, value = 1f, transitionState = FINISHED))
+            assertThat(dozeAmount).isEqualTo(1f)
+
+            sendTransitionStep(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+            assertThat(dozeAmount).isEqualTo(1f)
+
+            sendTransitionStep(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+            assertThat(dozeAmount).isEqualTo(0.5f)
+
+            sendTransitionStep(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+            assertThat(dozeAmount).isEqualTo(0f)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun dozeAmount_updatedByDozeTransitionWhenAodDisabled() =
+        testScope.runTest {
+            val dozeAmount by collectLastValue(underTest.dozeAmount)
+
+            keyguardRepository.setAodAvailable(false)
+
+            sendTransitionStep(TransitionStep(to = DOZING, value = 0f, transitionState = STARTED))
+            assertThat(dozeAmount).isEqualTo(0f)
+
+            sendTransitionStep(TransitionStep(to = DOZING, value = 0.5f, transitionState = RUNNING))
+            assertThat(dozeAmount).isEqualTo(0.5f)
+
+            sendTransitionStep(TransitionStep(to = DOZING, value = 1f, transitionState = FINISHED))
+            assertThat(dozeAmount).isEqualTo(1f)
+
+            sendTransitionStep(TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED))
+            assertThat(dozeAmount).isEqualTo(1f)
+
+            sendTransitionStep(TransitionStep(DOZING, LOCKSCREEN, 0.5f, RUNNING))
+            assertThat(dozeAmount).isEqualTo(0.5f)
+
+            sendTransitionStep(TransitionStep(DOZING, LOCKSCREEN, 1f, FINISHED))
+            assertThat(dozeAmount).isEqualTo(0f)
+        }
+
+    private suspend fun sendTransitionStep(step: TransitionStep) {
+        keyguardTransitionRepository.sendTransitionStep(step)
+        testScope.runCurrent()
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
new file mode 100644
index 0000000..2558d58
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractorTest.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.policy.IKeyguardDismissCallback
+import com.android.internal.policy.IKeyguardStateCallback
+import com.android.keyguard.trustManager
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.domain.interactor.keyguardStateCallbackInteractor
+import com.android.systemui.testKosmos
+import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.time.fakeSystemClock
+import kotlin.test.Test
+import kotlinx.coroutines.test.currentTime
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
+import org.mockito.kotlin.atLeast
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardStateCallbackInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private lateinit var underTest: KeyguardStateCallbackInteractor
+    private lateinit var callback: IKeyguardStateCallback
+    private lateinit var systemClock: FakeSystemClock
+
+    @Before
+    fun setUp() {
+        systemClock = kosmos.fakeSystemClock
+        systemClock.setCurrentTimeMillis(testScope.currentTime)
+
+        underTest = kosmos.keyguardStateCallbackInteractor
+        underTest.start()
+
+        callback = mock<IKeyguardStateCallback>()
+    }
+
+    @Test
+    fun test_addCallback_passesInitialValues() =
+        testScope.runTest {
+            underTest.addCallback(callback)
+
+            verify(callback).onShowingStateChanged(anyBoolean(), anyInt())
+            verify(callback).onInputRestrictedStateChanged(anyBoolean())
+            verify(callback).onTrustedChanged(anyBoolean())
+            verify(callback).onSimSecureStateChanged(anyBoolean())
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun test_lockscreenVisibility_notifyDismissSucceeded_ifNotVisible() =
+        testScope.runTest {
+            underTest.addCallback(callback)
+
+            val dismissCallback = mock<IKeyguardDismissCallback>()
+            kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+            runCurrent()
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope = testScope,
+            )
+
+            systemClock.advanceTime(1) // Required for DismissCallbackRegistry's bgExecutor
+            verify(dismissCallback).onDismissSucceeded()
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = testScope,
+            )
+
+            Mockito.verifyNoMoreInteractions(dismissCallback)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun test_lockscreenVisibility_reportsKeyguardShowingChanged() =
+        testScope.runTest {
+            underTest.addCallback(callback)
+
+            Mockito.clearInvocations(callback)
+            Mockito.clearInvocations(kosmos.trustManager)
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope = testScope,
+            )
+            runCurrent()
+
+            verify(callback, atLeastOnce()).onShowingStateChanged(eq(false), anyInt())
+            verify(kosmos.trustManager, atLeastOnce()).reportKeyguardShowingChanged()
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = testScope,
+            )
+
+            verify(callback, atLeastOnce()).onShowingStateChanged(eq(true), anyInt())
+            verify(kosmos.trustManager, atLeast(2)).reportKeyguardShowingChanged()
+        }
+}
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 77106ae..a617484 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
@@ -1465,10 +1465,8 @@
 
             // WHEN the keyguard is occluded and device wakes up and is no longer dreaming
             keyguardRepository.setDreaming(false)
-            testScheduler.advanceTimeBy(150) // The dreaming signal is debounced.
-            runCurrent()
             keyguardRepository.setKeyguardOccluded(true)
-            powerInteractor.setAwakeForTest()
+            testScheduler.advanceTimeBy(150) // The dreaming and occluded signals are debounced.
             runCurrent()
 
             // THEN a transition to OCCLUDED should occur
@@ -2059,12 +2057,7 @@
     fun glanceableHubToOccluded_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN device is not dreaming
-            powerInteractor.setAwakeForTest()
             keyguardRepository.setDreaming(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")
@@ -2073,6 +2066,7 @@
 
             // WHEN the keyguard is occluded
             keyguardRepository.setKeyguardOccluded(true)
+            advanceTimeBy(200.milliseconds)
             runCurrent()
 
             assertThat(transitionRepository)
@@ -2218,6 +2212,7 @@
             advanceTimeBy(10.milliseconds)
             keyguardRepository.setKeyguardOccluded(true)
             advanceTimeBy(200.milliseconds)
+            runCurrent()
 
             assertThat(transitionRepository)
                 .startedTransition(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index f31eb7f..309e3a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -27,11 +27,18 @@
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor
@@ -62,6 +69,7 @@
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val keyguardRepository = kosmos.fakeKeyguardRepository
+    val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
     val shadeRepository = kosmos.fakeShadeRepository
     val shadeTestUtil by lazy { kosmos.shadeTestUtil }
 
@@ -132,8 +140,15 @@
             assertThat(burnInOffsets?.x).isEqualTo(0)
 
             // WHEN we're in the middle of the doze amount change
-            keyguardRepository.setDozeAmount(.50f)
-            runCurrent()
+            if (SceneContainerFlag.isEnabled) {
+                sendTransitionSteps(
+                    TransitionStep(to = DOZING, value = 0.0f, transitionState = STARTED),
+                    TransitionStep(to = DOZING, value = 0.5f, transitionState = RUNNING),
+                )
+            } else {
+                keyguardRepository.setDozeAmount(.50f)
+                runCurrent()
+            }
 
             // THEN burn in is updated (between 0 and the full offset)
             assertThat(burnInOffsets?.progress).isGreaterThan(0f)
@@ -144,8 +159,14 @@
             assertThat(burnInOffsets?.x).isLessThan(burnInXOffset)
 
             // WHEN we're fully dozing
-            keyguardRepository.setDozeAmount(1f)
-            runCurrent()
+            if (SceneContainerFlag.isEnabled) {
+                sendTransitionSteps(
+                    TransitionStep(to = DOZING, value = 1.0f, transitionState = FINISHED)
+                )
+            } else {
+                keyguardRepository.setDozeAmount(1f)
+                runCurrent()
+            }
 
             // THEN burn in offsets are updated to final current values (for the given time)
             assertThat(burnInOffsets?.progress).isEqualTo(burnInProgress)
@@ -217,7 +238,9 @@
     }
 
     private fun setAwake() {
-        keyguardRepository.setDozeAmount(0f)
+        if (!SceneContainerFlag.isEnabled) {
+            keyguardRepository.setDozeAmount(0f)
+        }
         keyguardRepository.dozeTimeTick()
 
         bouncerRepository.setAlternateVisible(false)
@@ -225,4 +248,11 @@
         bouncerRepository.setPrimaryShow(false)
         powerInteractor.setAwakeForTest()
     }
+
+    private suspend fun sendTransitionSteps(vararg steps: TransitionStep) {
+        steps.forEach { step ->
+            keyguardTransitionRepository.sendTransitionStep(step)
+            testScope.runCurrent()
+        }
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index ff6ea3a..7f09370 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -91,12 +91,13 @@
         kosmos.fakeKeyguardClockRepository.setCurrentClock(clockController)
 
         underTest = kosmos.aodBurnInViewModel
+        underTest.updateBurnInParams(burnInParameters)
     }
 
     @Test
     fun movement_initializedToDefaultValues() =
         testScope.runTest {
-            val movement by collectLastValue(underTest.movement(burnInParameters))
+            val movement by collectLastValue(underTest.movement)
             assertThat(movement?.translationY).isEqualTo(0)
             assertThat(movement?.translationX).isEqualTo(0)
             assertThat(movement?.scale).isEqualTo(1f)
@@ -105,7 +106,7 @@
     @Test
     fun translationAndScale_whenNotDozing() =
         testScope.runTest {
-            val movement by collectLastValue(underTest.movement(burnInParameters))
+            val movement by collectLastValue(underTest.movement)
 
             // Set to not dozing (on lockscreen)
             keyguardTransitionRepository.sendTransitionStep(
@@ -128,10 +129,57 @@
         }
 
     @Test
+    fun translationX_aodToLockscreen() =
+        testScope.runTest {
+            underTest.updateBurnInParams(burnInParameters.copy(translationX = { -100f }))
+            val movement by collectLastValue(underTest.movement)
+            assertThat(movement?.translationX).isEqualTo(0)
+
+            // Trigger a change to the burn-in model
+            burnInFlow.value = BurnInModel(translationX = 20, translationY = 30, scale = 0.5f)
+
+            // Set to not dozing (on lockscreen)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.LOCKSCREEN,
+                    value = 0f,
+                    transitionState = TransitionState.STARTED,
+                ),
+                validateStep = false,
+            )
+            // Set to not dozing (on lockscreen)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.LOCKSCREEN,
+                    value = 0f,
+                    transitionState = TransitionState.RUNNING,
+                ),
+                validateStep = false,
+            )
+            assertThat(movement?.translationX).isEqualTo(-100)
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.LOCKSCREEN,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED,
+                ),
+                validateStep = false,
+            )
+
+            assertThat(movement?.translationX).isEqualTo(0)
+            assertThat(movement?.translationY).isEqualTo(0)
+            assertThat(movement?.scale).isEqualTo(1f)
+            assertThat(movement?.scaleClockOnly).isEqualTo(true)
+        }
+
+    @Test
     fun translationAndScale_whenFullyDozing() =
         testScope.runTest {
-            burnInParameters = burnInParameters.copy(minViewY = 100)
-            val movement by collectLastValue(underTest.movement(burnInParameters))
+            underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100))
+            val movement by collectLastValue(underTest.movement)
 
             // Set to dozing (on AOD)
             keyguardTransitionRepository.sendTransitionStep(
@@ -171,8 +219,8 @@
     @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() =
         testScope.runTest {
-            burnInParameters = burnInParameters.copy(minViewY = 100, topInset = 80)
-            val movement by collectLastValue(underTest.movement(burnInParameters))
+            underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
+            val movement by collectLastValue(underTest.movement)
 
             // Set to dozing (on AOD)
             keyguardTransitionRepository.sendTransitionStep(
@@ -213,8 +261,8 @@
     @EnableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() =
         testScope.runTest {
-            burnInParameters = burnInParameters.copy(minViewY = 100, topInset = 80)
-            val movement by collectLastValue(underTest.movement(burnInParameters))
+            underTest.updateBurnInParams(burnInParameters.copy(minViewY = 100, topInset = 80))
+            val movement by collectLastValue(underTest.movement)
 
             // Set to dozing (on AOD)
             keyguardTransitionRepository.sendTransitionStep(
@@ -256,7 +304,7 @@
         testScope.runTest {
             whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true)
 
-            val movement by collectLastValue(underTest.movement(burnInParameters))
+            val movement by collectLastValue(underTest.movement)
 
             // Set to dozing (on AOD)
             keyguardTransitionRepository.sendTransitionStep(
@@ -374,7 +422,7 @@
             whenever(clockController.config.useAlternateSmartspaceAODTransition)
                 .thenReturn(if (isWeatherClock) true else false)
 
-            val movement by collectLastValue(underTest.movement(burnInParameters))
+            val movement by collectLastValue(underTest.movement)
 
             // Set to dozing (on AOD)
             keyguardTransitionRepository.sendTransitionStep(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 2fd94e2..5186536 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -26,7 +26,6 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
-import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.communalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -47,10 +46,14 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shadeTestUtil
+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.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.dozeParameters
 import com.android.systemui.statusbar.phone.screenOffAnimationController
 import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.ui.isAnimating
 import com.android.systemui.util.ui.stopAnimating
@@ -70,11 +73,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-@EnableFlags(
-    FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
-    FLAG_NEW_AOD_TRANSITION,
-    FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
-)
+@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
 class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -110,6 +109,20 @@
     @Before
     fun setUp() {
         kosmos.sceneContainerRepository.setTransitionState(transitionState)
+
+        // Add sample notif so that the notif shelf has something to display
+        kosmos.activeNotificationListRepository.activeNotifications.value =
+            ActiveNotificationsStore.Builder()
+                .apply {
+                    addIndividualNotif(
+                        activeNotificationModel(
+                            key = "notif",
+                            aodIcon = mock(),
+                            groupKey = "testGroup",
+                        )
+                    )
+                }
+                .build()
     }
 
     @Test
@@ -129,7 +142,7 @@
                     from = KeyguardState.LOCKSCREEN,
                     to = KeyguardState.AOD,
                     value = 0f,
-                    transitionState = TransitionState.STARTED
+                    transitionState = TransitionState.STARTED,
                 ),
                 validateStep = false,
             )
@@ -393,7 +406,7 @@
                     flowOf(Scenes.Communal),
                     flowOf(0.5f),
                     false,
-                    emptyFlow()
+                    emptyFlow(),
                 )
 
             keyguardTransitionRepository.sendTransitionSteps(
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 88a1df1..3388c75 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,6 +16,7 @@
 
 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
@@ -25,6 +26,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -36,13 +38,14 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @EnableSceneContainer
+@EnableFlags(DualShade.FLAG_NAME)
 class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneInteractor = kosmos.sceneInteractor
 
-    private val underTest = kosmos.notificationsShadeOverlayContentViewModel
+    private val underTest by lazy { kosmos.notificationsShadeOverlayContentViewModel }
 
     @Test
     fun onScrimClicked_hidesShade() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/OWNERS
index cd04e82..d3eec97 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/OWNERS
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/OWNERS
@@ -1,3 +1,4 @@
 set noparent
 
 include /packages/SystemUI/src/com/android/systemui/qs/OWNERS
+include /packages/SystemUI/TEST_OWNERS
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 225adab..527aeaa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -136,6 +136,8 @@
     @Mock
     private ViewTreeObserver mViewTreeObserver;
 
+    private boolean mPagedTileLayoutListening = false;
+
     private TestableLongPressEffectProvider mLongPressEffectProvider =
             new TestableLongPressEffectProvider();
 
@@ -213,6 +215,11 @@
             return null;
         }).when(mQSPanel).setListening(anyBoolean());
 
+        doAnswer(invocation -> {
+            mPagedTileLayoutListening = invocation.getArgument(0);
+            return null;
+        }).when(mPagedTileLayout).setListening(anyBoolean(), any());
+
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSHost,
                 mQSCustomizerController, mMediaHost,
                 mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
@@ -571,6 +578,33 @@
         assertThat(mController.mRecords).isEmpty();
     }
 
+    @Test
+    public void listening_dettach_reAttach_listeningSetAgain() {
+        mController.setListening(true);
+
+        assertThat(mPagedTileLayoutListening).isTrue();
+
+        mController.onViewDetached();
+        assertThat(mPagedTileLayoutListening).isFalse();
+
+        mController.onViewAttached();
+        assertThat(mPagedTileLayoutListening).isTrue();
+    }
+
+    @Test
+    public void notListening_dettach_reAttach_stillNotListening() {
+        mController.setListening(false);
+
+        assertThat(mPagedTileLayoutListening).isFalse();
+
+        mController.onViewDetached();
+        assertThat(mPagedTileLayoutListening).isFalse();
+
+        mController.onViewAttached();
+        assertThat(mPagedTileLayoutListening).isFalse();
+    }
+
+
     private boolean usingMediaPlayer() {
         return !SceneContainerFlag.isEnabled();
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 7203b61..6f20e70 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -78,8 +78,6 @@
         Dispatchers.resetMain()
     }
 
-    // For now the state changes at 0.5f expansion. This will change once we implement animation
-    // (and this test will fail)
     @Test
     fun qsExpansionValueChanges_correctExpansionState() =
         with(kosmos) {
@@ -87,18 +85,27 @@
                 val expansionState by collectLastValue(underTest.expansionState)
 
                 underTest.qsExpansionValue = 0f
-                assertThat(expansionState)
-                    .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)
+                assertThat(expansionState!!.progress).isEqualTo(0f)
 
                 underTest.qsExpansionValue = 0.3f
-                assertThat(expansionState)
-                    .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)
-
-                underTest.qsExpansionValue = 0.7f
-                assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)
+                assertThat(expansionState!!.progress).isEqualTo(0.3f)
 
                 underTest.qsExpansionValue = 1f
-                assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)
+                assertThat(expansionState!!.progress).isEqualTo(1f)
+            }
+        }
+
+    @Test
+    fun qsExpansionValueChanges_clamped() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                val expansionState by collectLastValue(underTest.expansionState)
+
+                underTest.qsExpansionValue = -1f
+                assertThat(expansionState!!.progress).isEqualTo(0f)
+
+                underTest.qsExpansionValue = 2f
+                assertThat(expansionState!!.progress).isEqualTo(1f)
             }
         }
 
@@ -110,7 +117,7 @@
 
                 testableContext.orCreateTestableResources.addOverride(
                     R.bool.config_use_large_screen_shade_header,
-                    true
+                    true,
                 )
                 fakeConfigurationRepository.onConfigurationChange()
 
@@ -126,7 +133,7 @@
 
                 testableContext.orCreateTestableResources.addOverride(
                     R.bool.config_use_large_screen_shade_header,
-                    false
+                    false,
                 )
                 fakeConfigurationRepository.onConfigurationChange()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
index e58cf15..79a303d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
@@ -85,12 +85,12 @@
                 runCurrent()
 
                 // Assert that the tile is removed from the large tiles after resizing
-                underTest.resize(largeTile)
+                underTest.resize(largeTile, toIcon = true)
                 runCurrent()
                 assertThat(latest).doesNotContain(largeTile)
 
                 // Assert that the tile is added to the large tiles after resizing
-                underTest.resize(largeTile)
+                underTest.resize(largeTile, toIcon = false)
                 runCurrent()
                 assertThat(latest).contains(largeTile)
             }
@@ -122,7 +122,7 @@
                 val newTile = TileSpec.create("newTile")
 
                 // Remove the large tile from the current tiles
-                underTest.resize(newTile)
+                underTest.resize(newTile, toIcon = false)
                 runCurrent()
 
                 // Assert that it's still small
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index 484a8ff..3910903 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
 import com.android.systemui.qs.panels.ui.model.GridCell
-import com.android.systemui.qs.panels.ui.model.SpacerGridCell
 import com.android.systemui.qs.panels.ui.model.TileGridCell
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -39,13 +38,6 @@
     private val underTest = EditTileListState(TestEditTiles, 4)
 
     @Test
-    fun noDrag_listUnchanged() {
-        underTest.tiles.forEach { assertThat(it).isNotInstanceOf(SpacerGridCell::class.java) }
-        assertThat(underTest.tiles.map { (it as TileGridCell).tile.tileSpec })
-            .containsExactly(*TestEditTiles.map { it.tile.tileSpec }.toTypedArray())
-    }
-
-    @Test
     fun startDrag_listHasSpacers() {
         underTest.onStarted(TestEditTiles[0])
 
@@ -109,16 +101,6 @@
     }
 
     @Test
-    fun droppedNewTile_spacersDisappear() {
-        underTest.onStarted(TestEditTiles[0])
-        underTest.onDrop()
-
-        assertThat(underTest.tiles.toStrings()).isEqualTo(listOf("a", "b", "c", "d", "e"))
-        assertThat(underTest.isMoving(TestEditTiles[0].tile.tileSpec)).isFalse()
-        assertThat(underTest.dragInProgress).isFalse()
-    }
-
-    @Test
     fun movedTileOutOfBounds_tileDisappears() {
         underTest.onStarted(TestEditTiles[0])
         underTest.movedOutOfBounds()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt
new file mode 100644
index 0000000..4acf3ee
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt
@@ -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.systemui.qs.panels.ui.compose.selection
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MutableSelectionStateTest : SysuiTestCase() {
+    private val underTest = MutableSelectionState({}, {})
+
+    @Test
+    fun selectTile_isCorrectlySelected() {
+        assertThat(underTest.selection?.tileSpec).isNotEqualTo(TEST_SPEC)
+
+        underTest.select(TEST_SPEC, manual = true)
+        assertThat(underTest.selection?.tileSpec).isEqualTo(TEST_SPEC)
+        assertThat(underTest.selection?.manual).isTrue()
+
+        underTest.unSelect()
+        assertThat(underTest.selection).isNull()
+
+        val newSpec = TileSpec.create("newSpec")
+        underTest.select(TEST_SPEC, manual = true)
+        underTest.select(newSpec, manual = false)
+        assertThat(underTest.selection?.tileSpec).isNotEqualTo(TEST_SPEC)
+        assertThat(underTest.selection?.tileSpec).isEqualTo(newSpec)
+        assertThat(underTest.selection?.manual).isFalse()
+    }
+
+    @Test
+    fun startResize_createsResizingState() {
+        assertThat(underTest.resizingState).isNull()
+
+        // Resizing starts but no tile is selected
+        underTest.onResizingDragStart(TileWidths(0, 0, 1))
+        assertThat(underTest.resizingState).isNull()
+
+        // Resizing starts with a selected tile
+        underTest.select(TEST_SPEC, manual = true)
+        underTest.onResizingDragStart(TileWidths(0, 0, 1))
+
+        assertThat(underTest.resizingState).isNotNull()
+    }
+
+    @Test
+    fun endResize_clearsResizingState() {
+        val spec = TileSpec.create("testSpec")
+
+        // Resizing starts with a selected tile
+        underTest.select(spec, manual = true)
+        underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10))
+        assertThat(underTest.resizingState).isNotNull()
+
+        underTest.onResizingDragEnd()
+        assertThat(underTest.resizingState).isNull()
+    }
+
+    @Test
+    fun unselect_clearsResizingState() {
+        // Resizing starts with a selected tile
+        underTest.select(TEST_SPEC, manual = true)
+        underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10))
+        assertThat(underTest.resizingState).isNotNull()
+
+        underTest.unSelect()
+        assertThat(underTest.resizingState).isNull()
+    }
+
+    @Test
+    fun onResizingDrag_updatesResizingState() {
+        // Resizing starts with a selected tile
+        underTest.select(TEST_SPEC, manual = true)
+        underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10))
+        assertThat(underTest.resizingState).isNotNull()
+
+        underTest.onResizingDrag(5f)
+        assertThat(underTest.resizingState?.width).isEqualTo(5)
+
+        underTest.onResizingDrag(2f)
+        assertThat(underTest.resizingState?.width).isEqualTo(7)
+
+        underTest.onResizingDrag(-6f)
+        assertThat(underTest.resizingState?.width).isEqualTo(1)
+    }
+
+    @Test
+    fun onResizingDrag_receivesResizeCallback() {
+        var resized = false
+        val onResize: (TileSpec) -> Unit = {
+            assertThat(it).isEqualTo(TEST_SPEC)
+            resized = !resized
+        }
+        val underTest = MutableSelectionState(onResize = onResize, {})
+
+        // Resizing starts with a selected tile
+        underTest.select(TEST_SPEC, true)
+        underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10))
+        assertThat(underTest.resizingState).isNotNull()
+
+        // Drag under the threshold
+        underTest.onResizingDrag(1f)
+        assertThat(resized).isFalse()
+
+        // Drag over the threshold
+        underTest.onResizingDrag(5f)
+        assertThat(resized).isTrue()
+
+        // Drag back under the threshold
+        underTest.onResizingDrag(-5f)
+        assertThat(resized).isFalse()
+    }
+
+    @Test
+    fun onResizingEnded_receivesResizeEndCallback() {
+        var resizeEnded = false
+        val onResizeEnd: (TileSpec) -> Unit = { resizeEnded = true }
+        val underTest = MutableSelectionState({}, onResizeEnd = onResizeEnd)
+
+        // Resizing starts with a selected tile
+        underTest.select(TEST_SPEC, true)
+        underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10))
+
+        underTest.onResizingDragEnd()
+        assertThat(resizeEnded).isTrue()
+    }
+
+    @Test
+    fun onResizingEnded_setsSelectionAutomatically() {
+        val underTest = MutableSelectionState({}, {})
+
+        // Resizing starts with a selected tile
+        underTest.select(TEST_SPEC, manual = true)
+        underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10))
+
+        // Assert the selection was manual
+        assertThat(underTest.selection?.manual).isTrue()
+
+        underTest.onResizingDragEnd()
+
+        // Assert the selection is no longer manual due to the resizing
+        assertThat(underTest.selection?.manual).isFalse()
+    }
+
+    companion object {
+        private val TEST_SPEC = TileSpec.create("testSpec")
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingStateTest.kt
new file mode 100644
index 0000000..6e66783
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingStateTest.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.qs.panels.ui.compose.selection
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ResizingStateTest : SysuiTestCase() {
+
+    @Test
+    fun drag_updatesStateCorrectly() {
+        var resized = false
+        val underTest =
+            ResizingState(TileWidths(base = 0, min = 0, max = 10)) { resized = !resized }
+
+        assertThat(underTest.width).isEqualTo(0)
+
+        underTest.onDrag(2f)
+        assertThat(underTest.width).isEqualTo(2)
+
+        underTest.onDrag(1f)
+        assertThat(underTest.width).isEqualTo(3)
+        assertThat(resized).isTrue()
+
+        underTest.onDrag(-1f)
+        assertThat(underTest.width).isEqualTo(2)
+        assertThat(resized).isFalse()
+    }
+
+    @Test
+    fun dragOutOfBounds_isClampedCorrectly() {
+        val underTest = ResizingState(TileWidths(base = 0, min = 0, max = 10)) {}
+
+        assertThat(underTest.width).isEqualTo(0)
+
+        underTest.onDrag(100f)
+        assertThat(underTest.width).isEqualTo(10)
+
+        underTest.onDrag(-200f)
+        assertThat(underTest.width).isEqualTo(0)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 2580ac2..7798f46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.qs.tileimpl;
 
+import static com.android.systemui.Flags.FLAG_QS_NEW_TILES;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
@@ -21,11 +23,16 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.service.quicksettings.Tile;
 import android.testing.UiThreadTest;
 import android.widget.ImageView;
@@ -47,7 +54,6 @@
 @UiThreadTest
 @SmallTest
 public class QSIconViewImplTest extends SysuiTestCase {
-
     private QSIconViewImpl mIconView;
 
     @Before
@@ -106,6 +112,34 @@
         verify(iv).setImageTintList(argThat(stateList -> stateList.getColors()[0] == desiredColor));
     }
 
+
+    @EnableFlags(FLAG_QS_NEW_TILES)
+    @Test
+    public void testIconPreloaded_withFlagOn_immediatelyLoadsAll3TintColors() {
+        Context ctx = spy(mContext);
+
+        QSIconViewImpl iconView = new QSIconViewImpl(ctx);
+
+        verify(ctx, times(3)).obtainStyledAttributes(any());
+
+        iconView.getColor(new State()); // this should not increase the call count
+
+        verify(ctx, times(3)).obtainStyledAttributes(any());
+    }
+
+    @DisableFlags(FLAG_QS_NEW_TILES)
+    @Test
+    public void testIconPreloaded_withFlagOff_loadsOneTintColorAfterIconColorIsRead() {
+        Context ctx = spy(mContext);
+        QSIconViewImpl iconView = new QSIconViewImpl(ctx);
+
+        verify(ctx, never()).obtainStyledAttributes(any()); // none of the colors are preloaded
+
+        iconView.getColor(new State());
+
+        verify(ctx, times(1)).obtainStyledAttributes(any());
+    }
+
     @Test
     public void testStateSetCorrectly_toString() {
         ImageView iv = mock(ImageView.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index 620e90d..d32ba47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -17,13 +17,17 @@
 package com.android.systemui.qs.tiles.impl.internet.domain
 
 import android.graphics.drawable.TestStubDrawable
+import android.os.fakeExecutorHandler
 import android.widget.Switch
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.settingslib.graph.SignalDrawable
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.Text.Companion.loadText
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
@@ -31,6 +35,9 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -39,25 +46,93 @@
 class InternetTileMapperTest : SysuiTestCase() {
     private val kosmos = Kosmos()
     private val internetTileConfig = kosmos.qsInternetTileConfig
+    private val handler = kosmos.fakeExecutorHandler
     private val mapper by lazy {
         InternetTileMapper(
             context.orCreateTestableResources
                 .apply {
                     addOverride(R.drawable.ic_qs_no_internet_unavailable, TestStubDrawable())
+                    addOverride(R.drawable.ic_satellite_connected_2, TestStubDrawable())
                     addOverride(wifiRes, TestStubDrawable())
                 }
                 .resources,
             context.theme,
-            context
+            context,
+            handler,
         )
     }
 
     @Test
-    fun withActiveModel_mappedStateMatchesDataModel() {
+    fun withActiveCellularModel_mappedStateMatchesDataModel() {
         val inputModel =
             InternetTileModel.Active(
                 secondaryLabel = Text.Resource(R.string.quick_settings_networks_available),
-                iconId = wifiRes,
+                icon = InternetTileIconModel.Cellular(3),
+                stateDescription = null,
+                contentDescription =
+                    ContentDescription.Resource(R.string.quick_settings_internet_label),
+            )
+
+        val outputState = mapper.map(internetTileConfig, inputModel)
+
+        val signalDrawable = SignalDrawable(context, handler)
+        signalDrawable.setLevel(3)
+        val expectedState =
+            createInternetTileState(
+                QSTileState.ActivationState.ACTIVE,
+                context.getString(R.string.quick_settings_networks_available),
+                Icon.Loaded(signalDrawable, null),
+                null,
+                context.getString(R.string.quick_settings_internet_label),
+            )
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun withActiveSatelliteModel_mappedStateMatchesDataModel() {
+        val inputIcon =
+            SignalIconModel.Satellite(
+                3,
+                Icon.Resource(
+                    res = R.drawable.ic_satellite_connected_2,
+                    contentDescription =
+                        ContentDescription.Resource(
+                            R.string.accessibility_status_bar_satellite_good_connection
+                        ),
+                ),
+            )
+        val inputModel =
+            InternetTileModel.Active(
+                secondaryLabel = Text.Resource(R.string.quick_settings_networks_available),
+                icon = InternetTileIconModel.Satellite(inputIcon.icon),
+                stateDescription = null,
+                contentDescription =
+                    ContentDescription.Resource(
+                        R.string.accessibility_status_bar_satellite_good_connection
+                    ),
+            )
+
+        val outputState = mapper.map(internetTileConfig, inputModel)
+
+        val expectedSatIcon = SatelliteIconModel.fromSignalStrength(3)
+
+        val expectedState =
+            createInternetTileState(
+                QSTileState.ActivationState.ACTIVE,
+                inputModel.secondaryLabel.loadText(context).toString(),
+                Icon.Loaded(context.getDrawable(expectedSatIcon!!.res)!!, null),
+                expectedSatIcon.res,
+                expectedSatIcon.contentDescription.loadContentDescription(context).toString(),
+            )
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun withActiveWifiModel_mappedStateMatchesDataModel() {
+        val inputModel =
+            InternetTileModel.Active(
+                secondaryLabel = Text.Resource(R.string.quick_settings_networks_available),
+                icon = InternetTileIconModel.ResourceId(wifiRes),
                 stateDescription = null,
                 contentDescription =
                     ContentDescription.Resource(R.string.quick_settings_internet_label),
@@ -71,7 +146,7 @@
                 context.getString(R.string.quick_settings_networks_available),
                 Icon.Loaded(context.getDrawable(wifiRes)!!, contentDescription = null),
                 wifiRes,
-                context.getString(R.string.quick_settings_internet_label)
+                context.getString(R.string.quick_settings_internet_label),
             )
         QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
     }
@@ -81,7 +156,7 @@
         val inputModel =
             InternetTileModel.Inactive(
                 secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable),
-                iconId = R.drawable.ic_qs_no_internet_unavailable,
+                icon = InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable),
                 stateDescription = null,
                 contentDescription =
                     ContentDescription.Resource(R.string.quick_settings_networks_unavailable),
@@ -95,10 +170,10 @@
                 context.getString(R.string.quick_settings_networks_unavailable),
                 Icon.Loaded(
                     context.getDrawable(R.drawable.ic_qs_no_internet_unavailable)!!,
-                    contentDescription = null
+                    contentDescription = null,
                 ),
                 R.drawable.ic_qs_no_internet_unavailable,
-                context.getString(R.string.quick_settings_networks_unavailable)
+                context.getString(R.string.quick_settings_networks_unavailable),
             )
         QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
     }
@@ -107,7 +182,7 @@
         activationState: QSTileState.ActivationState,
         secondaryLabel: String,
         icon: Icon,
-        iconRes: Int,
+        iconRes: Int? = null,
         contentDescription: String,
     ): QSTileState {
         val label = context.getString(R.string.quick_settings_internet_label)
@@ -120,13 +195,13 @@
             setOf(
                 QSTileState.UserAction.CLICK,
                 QSTileState.UserAction.TOGGLE_CLICK,
-                QSTileState.UserAction.LONG_CLICK
+                QSTileState.UserAction.LONG_CLICK,
             ),
             contentDescription,
             null,
             QSTileState.SideViewIcon.Chevron,
             QSTileState.EnabledState.ENABLED,
-            Switch::class.qualifiedName
+            Switch::class.qualifiedName,
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 5a45060..5259aa8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -18,14 +18,12 @@
 
 import android.graphics.drawable.TestStubDrawable
 import android.os.UserHandle
-import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.AccessibilityContentDescriptions
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.shared.model.Text.Companion.loadText
 import com.android.systemui.coroutines.collectLastValue
@@ -49,6 +47,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -60,9 +59,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -144,7 +141,6 @@
         underTest =
             InternetTileDataInteractor(
                 context,
-                testScope.coroutineContext,
                 testScope.backgroundScope,
                 airplaneModeRepository,
                 connectivityRepository,
@@ -164,9 +160,11 @@
 
             connectivityRepository.defaultConnections.value = DefaultConnectionModel()
 
+            val expectedIcon =
+                InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable)
             assertThat(latest?.secondaryLabel)
                 .isEqualTo(Text.Resource(R.string.quick_settings_networks_unavailable))
-            assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_unavailable)
+            assertThat(latest?.icon).isEqualTo(expectedIcon)
         }
 
     @Test
@@ -183,11 +181,8 @@
                     underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
                 )
 
-            val networkModel =
-                WifiNetworkModel.Active.of(
-                    level = 4,
-                    ssid = "test ssid",
-                )
+            val networkModel = WifiNetworkModel.Active.of(level = 4, ssid = "test ssid")
+
             val wifiIcon =
                 WifiIcon.fromModel(model = networkModel, context = context, showHotspotInfo = true)
                     as WifiIcon.Visible
@@ -198,12 +193,9 @@
 
             assertThat(latest?.secondaryTitle).isEqualTo("test ssid")
             assertThat(latest?.secondaryLabel).isNull()
-            val expectedIcon =
-                Icon.Loaded(context.getDrawable(WifiIcons.WIFI_NO_INTERNET_ICONS[4])!!, null)
 
-            val actualIcon = latest?.icon
-            assertThat(actualIcon).isEqualTo(expectedIcon)
-            assertThat(latest?.iconId).isEqualTo(WifiIcons.WIFI_NO_INTERNET_ICONS[4])
+            val expectedIcon = InternetTileIconModel.ResourceId(WifiIcons.WIFI_NO_INTERNET_ICONS[4])
+            assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.contentDescription.loadContentDescription(context))
                 .isEqualTo("$internet,test ssid")
             val expectedSd = wifiIcon.contentDescription
@@ -229,8 +221,7 @@
             wifiRepository.setIsWifiDefault(true)
             wifiRepository.setWifiNetwork(networkModel)
 
-            val expectedIcon =
-                Icon.Loaded(context.getDrawable(WifiIcons.WIFI_NO_INTERNET_ICONS[4])!!, null)
+            val expectedIcon = InternetTileIconModel.ResourceId(WifiIcons.WIFI_NO_INTERNET_ICONS[4])
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
                 .doesNotContain(
@@ -249,9 +240,8 @@
             setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.TABLET)
 
             val expectedIcon =
-                Icon.Loaded(
-                    context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_tablet)!!,
-                    null
+                InternetTileIconModel.ResourceId(
+                    com.android.settingslib.R.drawable.ic_hotspot_tablet
                 )
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
@@ -271,9 +261,8 @@
             setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.LAPTOP)
 
             val expectedIcon =
-                Icon.Loaded(
-                    context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_laptop)!!,
-                    null
+                InternetTileIconModel.ResourceId(
+                    com.android.settingslib.R.drawable.ic_hotspot_laptop
                 )
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
@@ -293,10 +282,10 @@
             setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.WATCH)
 
             val expectedIcon =
-                Icon.Loaded(
-                    context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_watch)!!,
-                    null
+                InternetTileIconModel.ResourceId(
+                    com.android.settingslib.R.drawable.ic_hotspot_watch
                 )
+
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
                 .isEqualTo(
@@ -315,10 +304,7 @@
             setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.AUTO)
 
             val expectedIcon =
-                Icon.Loaded(
-                    context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_auto)!!,
-                    null
-                )
+                InternetTileIconModel.ResourceId(com.android.settingslib.R.drawable.ic_hotspot_auto)
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
                 .isEqualTo(
@@ -336,9 +322,8 @@
             setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.PHONE)
 
             val expectedIcon =
-                Icon.Loaded(
-                    context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_phone)!!,
-                    null
+                InternetTileIconModel.ResourceId(
+                    com.android.settingslib.R.drawable.ic_hotspot_phone
                 )
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
@@ -358,9 +343,8 @@
             setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.UNKNOWN)
 
             val expectedIcon =
-                Icon.Loaded(
-                    context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_phone)!!,
-                    null
+                InternetTileIconModel.ResourceId(
+                    com.android.settingslib.R.drawable.ic_hotspot_phone
                 )
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
@@ -380,10 +364,10 @@
             setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.INVALID)
 
             val expectedIcon =
-                Icon.Loaded(
-                    context.getDrawable(com.android.settingslib.R.drawable.ic_hotspot_phone)!!,
-                    null
+                InternetTileIconModel.ResourceId(
+                    com.android.settingslib.R.drawable.ic_hotspot_phone
                 )
+
             assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
                 .isEqualTo(
@@ -426,8 +410,9 @@
             assertThat(latest?.secondaryLabel).isNull()
             assertThat(latest?.secondaryTitle)
                 .isEqualTo(context.getString(R.string.quick_settings_networks_available))
-            assertThat(latest?.icon).isNull()
-            assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_available)
+            val expectedIcon =
+                InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_available)
+            assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription).isNull()
             val expectedCd =
                 "$internet,${context.getString(R.string.quick_settings_networks_available)}"
@@ -435,54 +420,19 @@
                 .isEqualTo(expectedCd)
         }
 
-    /**
-     * We expect a RuntimeException because [underTest] instantiates a SignalDrawable on the
-     * provided context, and so the SignalDrawable constructor attempts to instantiate a Handler()
-     * on the mentioned context. Since that context does not have a looper assigned to it, the
-     * handler instantiation will throw a RuntimeException.
-     *
-     * TODO(b/338068066): Robolectric behavior differs in that it does not throw the exception So
-     *   either we should make Robolectric behave similar to the device test, or change this test to
-     *   look for a different signal than the exception, when run by Robolectric. For now we just
-     *   assume the test is not Robolectric.
-     */
-    @Test(expected = java.lang.RuntimeException::class)
-    fun mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException() =
-        testScope.runTest {
-            assumeFalse(isRobolectricTest())
-
-            collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
-
-            connectivityRepository.setMobileConnected()
-            mobileConnectionsRepository.mobileIsDefault.value = true
-            mobileConnectionRepository.apply {
-                setAllLevels(3)
-                setAllRoaming(false)
-                networkName.value = NetworkNameModel.Default("test network")
-            }
-
-            runCurrent()
-        }
-
-    /**
-     * See [mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException] for description of the
-     * problem this test solves. The solution here is to assign a looper to the context via
-     * RunWithLooper. In the production code, the solution is to use a Main CoroutineContext for
-     * creating the SignalDrawable.
-     */
-    @TestableLooper.RunWithLooper
     @Test
-    fun mobileDefault_run_withLooper_usesNetworkNameAndIcon() =
+    fun mobileDefault_usesNetworkNameAndIcon() =
         testScope.runTest {
             val latest by
                 collectLastValue(
                     underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
                 )
+            val iconLevel = 3
 
             connectivityRepository.setMobileConnected()
             mobileConnectionsRepository.mobileIsDefault.value = true
             mobileConnectionRepository.apply {
-                setAllLevels(3)
+                setAllLevels(iconLevel)
                 setAllRoaming(false)
                 networkName.value = NetworkNameModel.Default("test network")
             }
@@ -491,8 +441,9 @@
             assertThat(latest?.secondaryTitle).isNotNull()
             assertThat(latest?.secondaryTitle.toString()).contains("test network")
             assertThat(latest?.secondaryLabel).isNull()
-            assertThat(latest?.icon).isInstanceOf(Icon.Loaded::class.java)
-            assertThat(latest?.iconId).isNull()
+            val expectedIcon = InternetTileIconModel.Cellular(iconLevel)
+
+            assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription.loadContentDescription(context))
                 .isEqualTo(latest?.secondaryTitle.toString())
             assertThat(latest?.contentDescription.loadContentDescription(context))
@@ -513,8 +464,8 @@
             assertThat(latest?.secondaryLabel.loadText(context))
                 .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context))
             assertThat(latest?.secondaryTitle).isNull()
-            assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet_fully)
-            assertThat(latest?.icon).isNull()
+            val expectedIcon = InternetTileIconModel.ResourceId(R.drawable.stat_sys_ethernet_fully)
+            assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription).isNull()
             assertThat(latest?.contentDescription.loadContentDescription(context))
                 .isEqualTo(latest?.secondaryLabel.loadText(context))
@@ -534,8 +485,8 @@
             assertThat(latest?.secondaryLabel.loadText(context))
                 .isEqualTo(ethernetIcon!!.contentDescription.loadContentDescription(context))
             assertThat(latest?.secondaryTitle).isNull()
-            assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet)
-            assertThat(latest?.icon).isNull()
+            val expectedIcon = InternetTileIconModel.ResourceId(R.drawable.stat_sys_ethernet)
+            assertThat(latest?.icon).isEqualTo(expectedIcon)
             assertThat(latest?.stateDescription).isNull()
             assertThat(latest?.contentDescription.loadContentDescription(context))
                 .isEqualTo(latest?.secondaryLabel.loadText(context))
@@ -543,11 +494,7 @@
 
     private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
         val networkModel =
-            WifiNetworkModel.Active.of(
-                level = 4,
-                ssid = "test ssid",
-                hotspotDeviceType = hotspot,
-            )
+            WifiNetworkModel.Active.of(level = 4, ssid = "test ssid", hotspotDeviceType = hotspot)
 
         connectivityRepository.setWifiConnected()
         wifiRepository.setIsWifiDefault(true)
@@ -560,7 +507,7 @@
         val NOT_CONNECTED_NETWORKS_UNAVAILABLE =
             InternetTileModel.Inactive(
                 secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable),
-                iconId = R.drawable.ic_qs_no_internet_unavailable,
+                icon = InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable),
                 stateDescription = null,
                 contentDescription =
                     ContentDescription.Resource(R.string.quick_settings_networks_unavailable),
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 abd1e2c..8c7ec47 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,6 +16,7 @@
 
 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
@@ -25,6 +26,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -35,13 +37,14 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @EnableSceneContainer
+@EnableFlags(DualShade.FLAG_NAME)
 class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneInteractor = kosmos.sceneInteractor
 
-    private val underTest = kosmos.quickSettingsShadeOverlayContentViewModel
+    private val underTest by lazy { kosmos.quickSettingsShadeOverlayContentViewModel }
 
     @Test
     fun onScrimClicked_hidesShade() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index a0cafcb..c9e958d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -341,7 +341,7 @@
             bouncerActionButton?.onClick?.invoke()
             runCurrent()
 
-            // TODO(b/298026988): Assert that an activity was started once we use ActivityStarter.
+            // TODO(b/369765704): Assert that an activity was started once we use ActivityStarter.
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilterTest.kt
new file mode 100644
index 0000000..efde1ec
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilterTest.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.scene.ui.viewmodel
+
+import android.graphics.Region
+import android.view.setSystemGestureExclusionRegion
+import androidx.compose.ui.geometry.Offset
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.sceneContainerGestureFilterFactory
+import com.android.systemui.settings.displayTracker
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SceneContainerGestureFilterTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val displayId = kosmos.displayTracker.defaultDisplayId
+
+    private val underTest = kosmos.sceneContainerGestureFilterFactory.create(displayId)
+    private val activationJob = Job()
+
+    @Test
+    fun shouldFilterGesture_whenNoRegion_returnsFalse() =
+        testScope.runTest {
+            activate()
+            setSystemGestureExclusionRegion(displayId, null)
+            runCurrent()
+
+            assertThat(underTest.shouldFilterGesture(Offset(100f, 100f))).isFalse()
+        }
+
+    @Test
+    fun shouldFilterGesture_whenOutsideRegion_returnsFalse() =
+        testScope.runTest {
+            activate()
+            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
+            runCurrent()
+
+            assertThat(underTest.shouldFilterGesture(Offset(300f, 100f))).isFalse()
+        }
+
+    @Test
+    fun shouldFilterGesture_whenInsideRegion_returnsTrue() =
+        testScope.runTest {
+            activate()
+            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
+            runCurrent()
+
+            assertThat(underTest.shouldFilterGesture(Offset(100f, 100f))).isTrue()
+        }
+
+    @Test(expected = IllegalStateException::class)
+    fun shouldFilterGesture_beforeActivation_throws() =
+        testScope.runTest {
+            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
+            runCurrent()
+
+            underTest.shouldFilterGesture(Offset(100f, 100f))
+        }
+
+    @Test(expected = IllegalStateException::class)
+    fun shouldFilterGesture_afterCancellation_throws() =
+        testScope.runTest {
+            activate()
+            setSystemGestureExclusionRegion(displayId, Region(0, 0, 200, 200))
+            runCurrent()
+
+            cancel()
+
+            underTest.shouldFilterGesture(Offset(100f, 100f))
+        }
+
+    private fun TestScope.activate() {
+        underTest.activateIn(testScope, activationJob)
+        runCurrent()
+    }
+
+    private fun TestScope.cancel() {
+        activationJob.cancel()
+        runCurrent()
+    }
+}
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 a0bb017..e60e742 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
@@ -36,10 +36,12 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.fakeOverlaysByKeys
 import com.android.systemui.scene.sceneContainerConfig
+import com.android.systemui.scene.sceneContainerGestureFilterFactory
 import com.android.systemui.scene.shared.logger.sceneLogger
 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.settings.displayTracker
 import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shared.flag.DualShade
@@ -86,6 +88,8 @@
                 shadeInteractor = kosmos.shadeInteractor,
                 splitEdgeDetector = kosmos.splitEdgeDetector,
                 logger = kosmos.sceneLogger,
+                gestureFilterFactory = kosmos.sceneContainerGestureFilterFactory,
+                displayId = kosmos.displayTracker.defaultDisplayId,
                 motionEventHandlerReceiver = { motionEventHandler ->
                     this@SceneContainerViewModelTest.motionEventHandler = motionEventHandler
                 },
@@ -283,10 +287,7 @@
             fakeSceneDataSource.showOverlay(Overlays.NotificationsShade)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays)
-                .containsExactly(
-                    Overlays.QuickSettingsShade,
-                    Overlays.NotificationsShade,
-                )
+                .containsExactly(Overlays.QuickSettingsShade, Overlays.NotificationsShade)
 
             val actionableContentKey =
                 underTest.getActionableContentKey(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index d163abf..19ac0cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -287,7 +287,7 @@
 
     @Test
     fun anyExpansion_shadeGreater() =
-        testScope.runTest() {
+        testScope.runTest {
             // WHEN shade is more expanded than QS
             shadeTestUtil.setShadeAndQsExpansion(.5f, 0f)
             runCurrent()
@@ -298,7 +298,7 @@
 
     @Test
     fun anyExpansion_qsGreater() =
-        testScope.runTest() {
+        testScope.runTest {
             // WHEN qs is more expanded than shade
             shadeTestUtil.setShadeAndQsExpansion(0f, .5f)
             runCurrent()
@@ -308,6 +308,36 @@
         }
 
     @Test
+    fun isShadeAnyExpanded_shadeCollapsed() =
+        testScope.runTest {
+            val isShadeAnyExpanded by collectLastValue(underTest.isShadeAnyExpanded)
+            shadeTestUtil.setShadeExpansion(0f)
+            runCurrent()
+
+            assertThat(isShadeAnyExpanded).isFalse()
+        }
+
+    @Test
+    fun isShadeAnyExpanded_shadePartiallyExpanded() =
+        testScope.runTest {
+            val isShadeAnyExpanded by collectLastValue(underTest.isShadeAnyExpanded)
+            shadeTestUtil.setShadeExpansion(0.01f)
+            runCurrent()
+
+            assertThat(isShadeAnyExpanded).isTrue()
+        }
+
+    @Test
+    fun isShadeAnyExpanded_shadeFullyExpanded() =
+        testScope.runTest {
+            val isShadeAnyExpanded by collectLastValue(underTest.isShadeAnyExpanded)
+            shadeTestUtil.setShadeExpansion(1f)
+            runCurrent()
+
+            assertThat(isShadeAnyExpanded).isTrue()
+        }
+
+    @Test
     fun isShadeTouchable_isFalse_whenDeviceAsleepAndNotPulsing() =
         testScope.runTest {
             powerRepository.updateWakefulness(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
index 109cd05..4592b60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
@@ -35,6 +35,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -382,4 +383,44 @@
             // THEN user is not interacting
             assertThat(actual).isFalse()
         }
+
+    @Test
+    fun expandNotificationsShade_unsupported() =
+        testScope.runTest {
+            assertThrows(UnsupportedOperationException::class.java) {
+                underTest.expandNotificationsShade("reason")
+            }
+        }
+
+    @Test
+    fun expandQuickSettingsShade_unsupported() =
+        testScope.runTest {
+            assertThrows(UnsupportedOperationException::class.java) {
+                underTest.expandQuickSettingsShade("reason")
+            }
+        }
+
+    @Test
+    fun collapseNotificationsShade_unsupported() =
+        testScope.runTest {
+            assertThrows(UnsupportedOperationException::class.java) {
+                underTest.collapseNotificationsShade("reason")
+            }
+        }
+
+    @Test
+    fun collapseQuickSettingsShade_unsupported() =
+        testScope.runTest {
+            assertThrows(UnsupportedOperationException::class.java) {
+                underTest.collapseQuickSettingsShade("reason")
+            }
+        }
+
+    @Test
+    fun collapseEitherShade_unsupported() =
+        testScope.runTest {
+            assertThrows(UnsupportedOperationException::class.java) {
+                underTest.collapseEitherShade("reason")
+            }
+        }
 }
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 f6fe667ff..eb8ea8b 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,12 +1,15 @@
 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
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -18,7 +21,9 @@
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.plugins.activityStarter
 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.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
 import com.android.systemui.testKosmos
@@ -48,7 +53,7 @@
     private val sceneInteractor = kosmos.sceneInteractor
     private val deviceEntryInteractor = kosmos.deviceEntryInteractor
 
-    private val underTest: ShadeHeaderViewModel = kosmos.shadeHeaderViewModel
+    private val underTest by lazy { kosmos.shadeHeaderViewModel }
 
     @Before
     fun setUp() {
@@ -96,6 +101,7 @@
         }
 
     @Test
+    @DisableFlags(DualShade.FLAG_NAME)
     fun onSystemIconContainerClicked_locked_collapsesShadeToLockscreen() =
         testScope.runTest {
             setDeviceEntered(false)
@@ -108,6 +114,25 @@
         }
 
     @Test
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun onSystemIconContainerClicked_lockedOnDualShade_collapsesShadeToLockscreen() =
+        testScope.runTest {
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            setDeviceEntered(false)
+            setScene(Scenes.Lockscreen)
+            setOverlay(Overlays.NotificationsShade)
+            assertThat(currentOverlays).isNotEmpty()
+
+            underTest.onSystemIconContainerClicked()
+            runCurrent()
+
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
     fun onSystemIconContainerClicked_unlocked_collapsesShadeToGone() =
         testScope.runTest {
             setDeviceEntered(true)
@@ -119,6 +144,24 @@
             assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
         }
 
+    @Test
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun onSystemIconContainerClicked_unlockedOnDualShade_collapsesShadeToGone() =
+        testScope.runTest {
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            setDeviceEntered(true)
+            setScene(Scenes.Gone)
+            setOverlay(Overlays.NotificationsShade)
+            assertThat(currentOverlays).isNotEmpty()
+
+            underTest.onSystemIconContainerClicked()
+            runCurrent()
+
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            assertThat(currentOverlays).isEmpty()
+        }
+
     companion object {
         private val SUB_1 =
             SubscriptionModel(
@@ -144,6 +187,17 @@
         testScope.runCurrent()
     }
 
+    private fun setOverlay(key: OverlayKey) {
+        val currentOverlays = sceneInteractor.currentOverlays.value + key
+        sceneInteractor.showOverlay(key, "test")
+        sceneInteractor.setTransitionState(
+            MutableStateFlow<ObservableTransitionState>(
+                ObservableTransitionState.Idle(sceneInteractor.currentScene.value, currentOverlays)
+            )
+        )
+        testScope.runCurrent()
+    }
+
     private fun TestScope.setDeviceEntered(isEntered: Boolean) {
         if (isEntered) {
             // Unlock the device marking the device has entered.
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 f72a2e8..aefbc6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
@@ -107,6 +108,7 @@
                     uiEventLogger,
                     { kosmos.interactionJankMonitor },
                     JavaAdapter(testScope.backgroundScope),
+                    { kosmos.keyguardInteractor },
                     { kosmos.keyguardTransitionInteractor },
                     { kosmos.shadeInteractor },
                     { kosmos.deviceUnlockedInteractor },
@@ -139,6 +141,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun testSetDozeAmountInternal_onlySetsOnce() {
         val listener = mock(StatusBarStateController.StateListener::class.java)
         underTest.addCallback(listener)
@@ -190,6 +193,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun testSetDozeAmount_immediatelyChangesDozeAmount_lockscreenTransitionFromAod() {
         // Put controller in AOD state
         underTest.setAndInstrumentDozeAmount(null, 1f, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
index 7b87aeb..d772e3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt
@@ -42,7 +42,8 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.domain.interactor.lockScreenNotificationMinimalismSetting
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
 import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.FakeSettings
@@ -66,7 +67,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@EnableFlags(NotificationMinimalismPrototype.FLAG_NAME)
+@EnableFlags(NotificationMinimalism.FLAG_NAME)
 class LockScreenMinimalismCoordinatorTest : SysuiTestCase() {
 
     private val kosmos =
@@ -76,7 +77,7 @@
                 mock<SysuiStatusBarStateController>().also { mock ->
                     doAnswer { statusBarState.ordinal }.whenever(mock).state
                 }
-            fakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
+            lockScreenNotificationMinimalismSetting = true
         }
     private val notifPipeline: NotifPipeline = mock()
     private var statusBarState: StatusBarState = StatusBarState.KEYGUARD
@@ -193,7 +194,7 @@
             kosmos.activeNotificationListRepository.topUnseenNotificationKey.value = child2.key
             assertThat(promoter.shouldPromoteToTopLevel(child1)).isFalse()
             assertThat(promoter.shouldPromoteToTopLevel(child2))
-                .isEqualTo(NotificationMinimalismPrototype.ungroupTopUnseen)
+                .isEqualTo(NotificationMinimalism.ungroupTopUnseen)
             assertThat(promoter.shouldPromoteToTopLevel(child3)).isFalse()
             assertThat(promoter.shouldPromoteToTopLevel(parent)).isFalse()
 
@@ -201,7 +202,7 @@
             kosmos.activeNotificationListRepository.topUnseenNotificationKey.value = child2.key
             assertThat(promoter.shouldPromoteToTopLevel(child1)).isTrue()
             assertThat(promoter.shouldPromoteToTopLevel(child2))
-                .isEqualTo(NotificationMinimalismPrototype.ungroupTopUnseen)
+                .isEqualTo(NotificationMinimalism.ungroupTopUnseen)
             assertThat(promoter.shouldPromoteToTopLevel(child3)).isFalse()
             assertThat(promoter.shouldPromoteToTopLevel(parent)).isFalse()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
index 2159b86..ea2e25e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -57,7 +57,7 @@
     }
 
     @Test
-    @EnableFlags(NotificationMinimalismPrototype.FLAG_NAME)
+    @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun topOngoingAndUnseenNotification() = runTest {
         val entry1 = NotificationEntryBuilder().setTag("entry1").build()
         val entry2 = NotificationEntryBuilder().setTag("entry2").build()
@@ -91,4 +91,17 @@
         testScheduler.runCurrent()
         assertThat(settingEnabled).isTrue()
     }
+
+    fun testLockScreenNotificationMinimalismSetting() = runTest {
+        val settingEnabled by
+            collectLastValue(underTest.isLockScreenNotificationMinimalismEnabled())
+
+        kosmos.lockScreenNotificationMinimalismSetting = false
+        testScheduler.runCurrent()
+        assertThat(settingEnabled).isFalse()
+
+        kosmos.lockScreenNotificationMinimalismSetting = true
+        testScheduler.runCurrent()
+        assertThat(settingEnabled).isTrue()
+    }
 }
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
new file mode 100644
index 0000000..28857a0
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
@@ -0,0 +1,290 @@
+/*
+ * 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.emptyshade.ui.viewmodel
+
+import android.app.Flags
+import android.app.NotificationManager.Policy
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.provider.Settings
+import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST
+import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.data.repository.updateNotificationPolicy
+import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
+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.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(ParameterizedAndroidJunit4::class)
+@SmallTest
+@EnableFlags(FooterViewRefactor.FLAG_NAME)
+class EmptyShadeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val zenModeRepository = kosmos.zenModeRepository
+    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
+    private val fakeSecureSettingsRepository = kosmos.fakeSecureSettingsRepository
+
+    private val underTest = kosmos.emptyShadeViewModel
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    @Test
+    fun areNotificationsHiddenInShade_true() =
+        testScope.runTest {
+            val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+            zenModeRepository.updateNotificationPolicy(
+                suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+            )
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+            runCurrent()
+
+            assertThat(hidden).isTrue()
+        }
+
+    @Test
+    fun areNotificationsHiddenInShade_false() =
+        testScope.runTest {
+            val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+            zenModeRepository.updateNotificationPolicy(
+                suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+            )
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
+            runCurrent()
+
+            assertThat(hidden).isFalse()
+        }
+
+    @Test
+    fun hasFilteredOutSeenNotifications_true() =
+        testScope.runTest {
+            val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications)
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
+            runCurrent()
+
+            assertThat(hasFilteredNotifs).isTrue()
+        }
+
+    @Test
+    fun hasFilteredOutSeenNotifications_false() =
+        testScope.runTest {
+            val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications)
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
+            runCurrent()
+
+            assertThat(hasFilteredNotifs).isFalse()
+        }
+
+    @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @DisableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
+    fun text_changesWhenNotifsHiddenInShade() =
+        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")
+
+            zenModeRepository.updateNotificationPolicy(
+                suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+            )
+            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+            runCurrent()
+
+            assertThat(text).isEqualTo("Notifications paused by Do Not Disturb")
+        }
+
+    @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
+    fun text_reflectsModesHidingNotifications() =
+        testScope.runTest {
+            val text by collectLastValue(underTest.text)
+
+            assertThat(text).isEqualTo("No notifications")
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setId("Do not disturb")
+                    .setName("Do not disturb")
+                    .setActive(true)
+                    .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                    .build()
+            )
+            runCurrent()
+            assertThat(text).isEqualTo("Notifications paused by Do not disturb")
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setId("Work")
+                    .setName("Work")
+                    .setActive(true)
+                    .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                    .build()
+            )
+            runCurrent()
+            assertThat(text).isEqualTo("Notifications paused by Do not disturb and one other mode")
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setId("Gym")
+                    .setName("Gym")
+                    .setActive(true)
+                    .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                    .build()
+            )
+            runCurrent()
+            assertThat(text).isEqualTo("Notifications paused by Do not disturb and 2 other modes")
+
+            zenModeRepository.deactivateMode("Do not disturb")
+            zenModeRepository.deactivateMode("Work")
+            runCurrent()
+            assertThat(text).isEqualTo("Notifications paused by Gym")
+        }
+
+    @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    fun footer_isVisibleWhenSeenNotifsAreFilteredOut() =
+        testScope.runTest {
+            val footerVisible by collectLastValue(underTest.footer.isVisible)
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
+            runCurrent()
+
+            assertThat(footerVisible).isFalse()
+
+            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
+            runCurrent()
+
+            assertThat(footerVisible).isTrue()
+        }
+
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @Test
+    fun onClick_whenHistoryDisabled_leadsToSettingsPage() =
+        testScope.runTest {
+            val onClick by collectLastValue(underTest.onClick)
+            runCurrent()
+
+            fakeSecureSettingsRepository.setInt(Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0)
+
+            assertThat(onClick?.targetIntent?.action)
+                .isEqualTo(Settings.ACTION_NOTIFICATION_SETTINGS)
+            assertThat(onClick?.backStack).isEmpty()
+        }
+
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @Test
+    fun onClick_whenHistoryEnabled_leadsToHistoryPage() =
+        testScope.runTest {
+            val onClick by collectLastValue(underTest.onClick)
+            runCurrent()
+
+            fakeSecureSettingsRepository.setInt(Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 1)
+
+            assertThat(onClick?.targetIntent?.action)
+                .isEqualTo(Settings.ACTION_NOTIFICATION_HISTORY)
+            assertThat(onClick?.backStack?.map { it.action })
+                .containsExactly(Settings.ACTION_NOTIFICATION_SETTINGS)
+        }
+
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @Test
+    fun onClick_whenOneModeHidingNotifications_leadsToModeSettings() =
+        testScope.runTest {
+            val onClick by collectLastValue(underTest.onClick)
+            runCurrent()
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setId("ID")
+                    .setActive(true)
+                    .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                    .build()
+            )
+            runCurrent()
+
+            assertThat(onClick?.targetIntent?.action)
+                .isEqualTo(Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+            assertThat(
+                    onClick?.targetIntent?.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID)
+                )
+                .isEqualTo("ID")
+            assertThat(onClick?.backStack?.map { it.action })
+                .containsExactly(Settings.ACTION_ZEN_MODE_SETTINGS)
+        }
+
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @Test
+    fun onClick_whenMultipleModesHidingNotifications_leadsToGeneralModesSettings() =
+        testScope.runTest {
+            val onClick by collectLastValue(underTest.onClick)
+            runCurrent()
+
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setActive(true)
+                    .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                    .build()
+            )
+            zenModeRepository.addMode(
+                TestModeBuilder()
+                    .setActive(true)
+                    .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                    .build()
+            )
+            runCurrent()
+
+            assertThat(onClick?.targetIntent?.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
+            assertThat(onClick?.backStack).isEmpty()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 26e1a4d..d12d6f6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -18,12 +18,9 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import android.app.NotificationManager.Policy
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
-import android.provider.Settings
 import androidx.test.filters.SmallTest
-import com.android.settingslib.notification.data.repository.updateNotificationPolicy
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.DisableSceneContainer
@@ -46,7 +43,6 @@
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository
-import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
 import com.android.systemui.statusbar.policy.fakeConfigurationController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.ui.isAnimating
@@ -79,7 +75,6 @@
     private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository
     private val fakeUserSetupRepository = kosmos.fakeUserSetupRepository
     private val headsUpRepository = kosmos.headsUpNotificationRepository
-    private val zenModeRepository = kosmos.zenModeRepository
 
     private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
 
@@ -266,56 +261,6 @@
         }
 
     @Test
-    fun areNotificationsHiddenInShade_true() =
-        testScope.runTest {
-            val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
-
-            zenModeRepository.updateNotificationPolicy(
-                suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
-            )
-            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
-            runCurrent()
-
-            assertThat(hidden).isTrue()
-        }
-
-    @Test
-    fun areNotificationsHiddenInShade_false() =
-        testScope.runTest {
-            val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
-
-            zenModeRepository.updateNotificationPolicy(
-                suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
-            )
-            zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
-            runCurrent()
-
-            assertThat(hidden).isFalse()
-        }
-
-    @Test
-    fun hasFilteredOutSeenNotifications_true() =
-        testScope.runTest {
-            val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications)
-
-            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true
-            runCurrent()
-
-            assertThat(hasFilteredNotifs).isTrue()
-        }
-
-    @Test
-    fun hasFilteredOutSeenNotifications_false() =
-        testScope.runTest {
-            val hasFilteredNotifs by collectLastValue(underTest.hasFilteredOutSeenNotifications)
-
-            activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false
-            runCurrent()
-
-            assertThat(hasFilteredNotifs).isFalse()
-        }
-
-    @Test
     fun shouldIncludeFooterView_trueWhenShade() =
         testScope.runTest {
             val shouldIncludeFooterView by collectFooterViewVisibility()
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 425f16e..add7ac9 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
@@ -44,6 +44,7 @@
 import com.android.systemui.keyguard.shared.model.BurnInModel
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -54,7 +55,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
-import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
 import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
@@ -69,7 +69,6 @@
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -154,7 +153,7 @@
     fun setUp() {
         shadeTestUtil.setSplitShade(false)
         movementFlow = MutableStateFlow(BurnInModel())
-        whenever(aodBurnInViewModel.movement(any())).thenReturn(movementFlow)
+        whenever(aodBurnInViewModel.movement).thenReturn(movementFlow)
         underTest = kosmos.sharedNotificationContainerViewModel
     }
 
@@ -363,6 +362,69 @@
             showLockscreen()
             assertThat(alpha).isEqualTo(1f)
 
+            // Go to dozing
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = LOCKSCREEN,
+                to = DOZING,
+                testScope,
+            )
+            assertThat(alpha).isEqualTo(1f)
+
+            // Start transitioning to glanceable hub
+            val progress = 0.6f
+            kosmos.setTransition(
+                sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.STARTED,
+                        from = DOZING,
+                        to = GLANCEABLE_HUB,
+                        value = 0f,
+                    ),
+            )
+            runCurrent()
+            kosmos.setTransition(
+                sceneTransition =
+                    Transition(
+                        from = Scenes.Lockscreen,
+                        to = Scenes.Communal,
+                        progress = flowOf(progress),
+                    ),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.RUNNING,
+                        from = DOZING,
+                        to = GLANCEABLE_HUB,
+                        value = progress,
+                    ),
+            )
+            runCurrent()
+            // Keep notifications hidden during the transition from dream to hub
+            assertThat(alpha).isEqualTo(0)
+
+            // Finish transition to glanceable hub
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.FINISHED,
+                        from = DOZING,
+                        to = GLANCEABLE_HUB,
+                        value = 1f,
+                    ),
+            )
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
+    fun glanceableHubAlpha_dozingToHub() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.glanceableHubAlpha)
+
+            // Start on lockscreen, notifications should be unhidden.
+            showLockscreen()
+            assertThat(alpha).isEqualTo(1f)
+
             // Transition to dream, notifications should be hidden so that transition
             // from dream->hub doesn't cause notification flicker.
             showDream()
@@ -746,7 +808,7 @@
     @DisableSceneContainer
     fun translationYUpdatesOnKeyguardForBurnIn() =
         testScope.runTest {
-            val translationY by collectLastValue(underTest.translationY(BurnInParameters()))
+            val translationY by collectLastValue(underTest.translationY)
 
             showLockscreen()
             assertThat(translationY).isEqualTo(0)
@@ -759,7 +821,7 @@
     @DisableSceneContainer
     fun translationYUpdatesOnKeyguard() =
         testScope.runTest {
-            val translationY by collectLastValue(underTest.translationY(BurnInParameters()))
+            val translationY by collectLastValue(underTest.translationY)
 
             configurationRepository.setDimensionPixelSize(
                 R.dimen.keyguard_translate_distance_on_swipe_up,
@@ -780,7 +842,7 @@
     @DisableSceneContainer
     fun translationYDoesNotUpdateWhenShadeIsExpanded() =
         testScope.runTest {
-            val translationY by collectLastValue(underTest.translationY(BurnInParameters()))
+            val translationY by collectLastValue(underTest.translationY)
 
             configurationRepository.setDimensionPixelSize(
                 R.dimen.keyguard_translate_distance_on_swipe_up,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index 0f6dc07..c5ccf9e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -25,6 +25,7 @@
 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
 import android.service.notification.SystemZenRules
+import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
@@ -34,6 +35,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.shared.settings.data.repository.secureSettingsRepository
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
 import com.android.systemui.testKosmos
@@ -379,4 +381,46 @@
 
             assertThat(dndMode!!.isActive).isTrue()
         }
+
+    @Test
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
+    fun modesHidingNotifications_onlyIncludesModesWithNotifListSuppression() =
+        testScope.runTest {
+            val modesHidingNotifications by collectLastValue(underTest.modesHidingNotifications)
+
+            zenModeRepository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("Not active, no list suppression")
+                        .setActive(false)
+                        .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Not active, has list suppression")
+                        .setActive(false)
+                        .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("No list suppression")
+                        .setActive(true)
+                        .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Has list suppression 1")
+                        .setActive(true)
+                        .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Has list suppression 2")
+                        .setActive(true)
+                        .setVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, /* allowed= */ false)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(modesHidingNotifications?.map { it.name })
+                .containsExactly("Has list suppression 1", "Has list suppression 2")
+                .inOrder()
+        }
 }
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index 682a68f..6212e2b 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -23,15 +23,16 @@
 }
 
 java_library {
-
     name: "SystemUIPluginLib",
-
     srcs: [
         "bcsmartspace/src/**/*.java",
         "bcsmartspace/src/**/*.kt",
         "src/**/*.java",
         "src/**/*.kt",
     ],
+    exclude_srcs: [
+        "src/**/PluginProtectorStub.kt",
+    ],
 
     optimize: {
         proguard_flags_files: [
@@ -40,6 +41,8 @@
         export_proguard_flags_files: true,
     },
 
+    plugins: ["PluginAnnotationProcessor"],
+
     // If you add a static lib here, you may need to also add the package to the ClassLoaderFilter
     // in PluginInstance. That will ensure that loaded plugins have access to the related classes.
     // You should also add it to proguard_common.flags so that proguard does not remove the portions
@@ -53,7 +56,6 @@
         "SystemUILogLib",
         "androidx.annotation_annotation",
     ],
-
 }
 
 android_app {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginProtectorStub.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginProtectorStub.kt
new file mode 100644
index 0000000..172b049
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginProtectorStub.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.plugins
+
+/**
+ * Stub version of PluginProtector that is normally generated by [ProtectedPluginProcessor].
+ * Intended for use in applications where kapt cannot be used, but disables the protection feature.
+ */
+class PluginProtector {
+    companion object {
+        @JvmStatic
+        fun <T> tryProtect(target: T, listener: ProtectedPluginListener): T? {
+            return null
+        }
+
+        @JvmStatic
+        fun <T> protectIfAble(target: T, listener: ProtectedPluginListener): T {
+            return target
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/TestPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/TestPlugin.kt
new file mode 100644
index 0000000..33f7b7a
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/TestPlugin.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.plugins
+
+import com.android.systemui.plugins.annotations.ProtectedInterface
+import com.android.systemui.plugins.annotations.ProtectedReturn
+import com.android.systemui.plugins.annotations.ProvidesInterface
+
+@ProtectedInterface
+@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
+/** Interface intended for use in tests */
+interface TestPlugin : Plugin {
+    companion object {
+        const val VERSION = 1
+
+        const val ACTION = "testAction"
+    }
+
+    @ProtectedReturn("return new Object();")
+    /** Test method, implemented by test */
+    fun methodThrowsError(): Object
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 8dc4815..6d27b6f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -21,7 +21,11 @@
 import com.android.internal.annotations.Keep
 import com.android.systemui.log.core.MessageBuffer
 import com.android.systemui.plugins.Plugin
+import com.android.systemui.plugins.annotations.GeneratedImport
+import com.android.systemui.plugins.annotations.ProtectedInterface
+import com.android.systemui.plugins.annotations.ProtectedReturn
 import com.android.systemui.plugins.annotations.ProvidesInterface
+import com.android.systemui.plugins.annotations.SimpleProperty
 import java.io.PrintWriter
 import java.util.Locale
 import java.util.TimeZone
@@ -31,6 +35,7 @@
 typealias ClockId = String
 
 /** A Plugin which exposes the ClockProvider interface */
+@ProtectedInterface
 @ProvidesInterface(action = ClockProviderPlugin.ACTION, version = ClockProviderPlugin.VERSION)
 interface ClockProviderPlugin : Plugin, ClockProvider {
     companion object {
@@ -40,31 +45,42 @@
 }
 
 /** Interface for building clocks and providing information about those clocks */
+@ProtectedInterface
+@GeneratedImport("java.util.List")
+@GeneratedImport("java.util.ArrayList")
 interface ClockProvider {
     /** Initializes the clock provider with debug log buffers */
     fun initialize(buffers: ClockMessageBuffers?)
 
+    @ProtectedReturn("return new ArrayList<ClockMetadata>();")
     /** Returns metadata for all clocks this provider knows about */
     fun getClocks(): List<ClockMetadata>
 
+    @ProtectedReturn("return null;")
     /** Initializes and returns the target clock design */
-    fun createClock(settings: ClockSettings): ClockController
+    fun createClock(settings: ClockSettings): ClockController?
 
+    @ProtectedReturn("return new ClockPickerConfig(\"\", \"\", \"\", null);")
     /** Settings configuration parameters for the clock */
     fun getClockPickerConfig(id: ClockId): ClockPickerConfig
 }
 
 /** Interface for controlling an active clock */
+@ProtectedInterface
 interface ClockController {
+    @get:SimpleProperty
     /** A small version of the clock, appropriate for smaller viewports */
     val smallClock: ClockFaceController
 
+    @get:SimpleProperty
     /** A large version of the clock, appropriate when a bigger viewport is available */
     val largeClock: ClockFaceController
 
+    @get:SimpleProperty
     /** Determines the way the hosting app should behave when rendering either clock face */
     val config: ClockConfig
 
+    @get:SimpleProperty
     /** Events that clocks may need to respond to */
     val events: ClockEvents
 
@@ -76,19 +92,26 @@
 }
 
 /** Interface for a specific clock face version rendered by the clock */
+@ProtectedInterface
 interface ClockFaceController {
+    @get:SimpleProperty
+    @Deprecated("Prefer use of layout")
     /** View that renders the clock face */
     val view: View
 
+    @get:SimpleProperty
     /** Layout specification for this clock */
     val layout: ClockFaceLayout
 
+    @get:SimpleProperty
     /** Determines the way the hosting app should behave when rendering this clock face */
     val config: ClockFaceConfig
 
+    @get:SimpleProperty
     /** Events specific to this clock face */
     val events: ClockFaceEvents
 
+    @get:SimpleProperty
     /** Triggers for various animations */
     val animations: ClockAnimations
 }
@@ -107,14 +130,21 @@
 
 data class AodClockBurnInModel(val scale: Float, val translationX: Float, val translationY: Float)
 
-/** Specifies layout information for the */
+/** Specifies layout information for the clock face */
+@ProtectedInterface
+@GeneratedImport("java.util.ArrayList")
+@GeneratedImport("android.view.View")
 interface ClockFaceLayout {
+    @get:ProtectedReturn("return new ArrayList<View>();")
     /** All clock views to add to the root constraint layout before applying constraints. */
     val views: List<View>
 
+    @ProtectedReturn("return constraints;")
     /** Custom constraints to apply to Lockscreen ConstraintLayout. */
     fun applyConstraints(constraints: ConstraintSet): ConstraintSet
 
+    @ProtectedReturn("return constraints;")
+    /** Custom constraints to apply to preview ConstraintLayout. */
     fun applyPreviewConstraints(constraints: ConstraintSet): ConstraintSet
 
     fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel)
@@ -145,7 +175,9 @@
 }
 
 /** Events that should call when various rendering parameters change */
+@ProtectedInterface
 interface ClockEvents {
+    @get:ProtectedReturn("return false;")
     /** Set to enable or disable swipe interaction */
     var isReactiveTouchInteractionEnabled: Boolean
 
@@ -187,6 +219,7 @@
 )
 
 /** Methods which trigger various clock animations */
+@ProtectedInterface
 interface ClockAnimations {
     /** Runs an enter animation (if any) */
     fun enter()
@@ -230,6 +263,7 @@
 }
 
 /** Events that have specific data about the related face */
+@ProtectedInterface
 interface ClockFaceEvents {
     /** Call every time tick */
     fun onTimeTick()
@@ -270,7 +304,9 @@
 /** Some data about a clock design */
 data class ClockMetadata(val clockId: ClockId)
 
-data class ClockPickerConfig(
+data class ClockPickerConfig
+@JvmOverloads
+constructor(
     val id: String,
 
     /** Localized name of the clock */
@@ -338,7 +374,7 @@
     /** Transition to AOD should move smartspace like large clock instead of small clock */
     val useAlternateSmartspaceAODTransition: Boolean = false,
 
-    /** Use ClockPickerConfig.isReactiveToTone instead */
+    /** Deprecated version of isReactiveToTone; moved to ClockPickerConfig */
     @Deprecated("TODO(b/352049256): Remove in favor of ClockPickerConfig.isReactiveToTone")
     val isReactiveToTone: Boolean = true,
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index be44dee..73626b4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -184,7 +184,10 @@
             }
         }
 
-        /** Get the text for secondaryLabel. */
+        /**
+         *  If the current secondaryLabel value is not empty, ignore the given input and return
+         *  the current value. Otherwise return current value.
+         */
         public CharSequence getSecondaryLabel(CharSequence stateText) {
             // Use a local reference as the value might change from other threads
             CharSequence localSecondaryLabel = secondaryLabel;
diff --git a/packages/SystemUI/plugin_core/Android.bp b/packages/SystemUI/plugin_core/Android.bp
index 521c019..98b5026 100644
--- a/packages/SystemUI/plugin_core/Android.bp
+++ b/packages/SystemUI/plugin_core/Android.bp
@@ -24,8 +24,13 @@
 
 java_library {
     sdk_version: "current",
-    name: "PluginCoreLib",
-    srcs: ["src/**/*.java"],
+    name: "PluginAnnotationLib",
+    host_supported: true,
+    device_supported: true,
+    srcs: [
+        "src/**/annotations/*.java",
+        "src/**/annotations/*.kt",
+    ],
     optimize: {
         proguard_flags_files: ["proguard.flags"],
         // Ensure downstream clients that reference this as a shared lib
@@ -37,3 +42,57 @@
     // no compatibility issues with launcher
     java_version: "1.8",
 }
+
+java_library {
+    sdk_version: "current",
+    name: "PluginCoreLib",
+    device_supported: true,
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    exclude_srcs: [
+        "src/**/annotations/*.java",
+        "src/**/annotations/*.kt",
+    ],
+    static_libs: [
+        "PluginAnnotationLib",
+    ],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+        // Ensure downstream clients that reference this as a shared lib
+        // inherit the appropriate flags to preserve annotations.
+        export_proguard_flags_files: true,
+    },
+
+    // Enforce that the library is built against java 8 so that there are
+    // no compatibility issues with launcher
+    java_version: "1.8",
+}
+
+java_library {
+    java_version: "1.8",
+    name: "PluginAnnotationProcessorLib",
+    host_supported: true,
+    device_supported: false,
+    srcs: [
+        "processor/src/**/*.java",
+        "processor/src/**/*.kt",
+    ],
+    plugins: ["auto_service_plugin"],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "auto_service_annotations",
+        "auto_common",
+        "PluginAnnotationLib",
+        "guava",
+        "jsr330",
+    ],
+}
+
+java_plugin {
+    name: "PluginAnnotationProcessor",
+    processor_class: "com.android.systemui.plugins.processor.ProtectedPluginProcessor",
+    static_libs: ["PluginAnnotationProcessorLib"],
+    java_version: "1.8",
+}
diff --git a/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/ProtectedPluginProcessor.kt b/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/ProtectedPluginProcessor.kt
new file mode 100644
index 0000000..6b54d89
--- /dev/null
+++ b/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/ProtectedPluginProcessor.kt
@@ -0,0 +1,356 @@
+/*
+ * 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.plugins.processor
+
+import com.android.systemui.plugins.annotations.GeneratedImport
+import com.android.systemui.plugins.annotations.ProtectedInterface
+import com.android.systemui.plugins.annotations.ProtectedReturn
+import com.android.systemui.plugins.annotations.SimpleProperty
+import com.google.auto.service.AutoService
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.ProcessingEnvironment
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.PackageElement
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.tools.Diagnostic.Kind
+import kotlin.collections.ArrayDeque
+
+/**
+ * [ProtectedPluginProcessor] generates a proxy implementation for interfaces annotated with
+ * [ProtectedInterface] which catches [LinkageError]s generated by the proxied target. This protects
+ * the plugin host from crashing due to out-of-date plugin code, where some call has changed so that
+ * the [ClassLoader] can no longer resolve it correctly.
+ *
+ * [PluginInstance] observes these failures via [ProtectedMethodListener] and unloads the plugin in
+ * question to prevent further issues. This persists through further load/unload requests.
+ *
+ * To centralize access to the proxy types, an additional type [PluginProtector] is also generated.
+ * This class provides static methods which wrap an instance of the target interface in the proxy
+ * type if it is not already an instance of the proxy.
+ */
+@AutoService(ProtectedPluginProcessor::class)
+class ProtectedPluginProcessor : AbstractProcessor() {
+    private lateinit var procEnv: ProcessingEnvironment
+
+    override fun init(procEnv: ProcessingEnvironment) {
+        this.procEnv = procEnv
+    }
+
+    override fun getSupportedAnnotationTypes(): Set<String> =
+        setOf("com.android.systemui.plugins.annotations.ProtectedInterface")
+
+    private data class TargetData(
+        val attribute: TypeElement,
+        val sourceType: Element,
+        val sourcePkg: String,
+        val sourceName: String,
+        val outputName: String,
+    )
+
+    override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
+        val targets = mutableMapOf<String, TargetData>() // keyed by fully-qualified source name
+        val additionalImports = mutableSetOf<String>()
+        for (attr in annotations) {
+            for (target in roundEnv.getElementsAnnotatedWith(attr)) {
+                val sourceName = "${target.simpleName}"
+                val outputName = "${sourceName}Protector"
+                val pkg = (target.getEnclosingElement() as PackageElement).qualifiedName.toString()
+                targets.put("$target", TargetData(attr, target, pkg, sourceName, outputName))
+
+                // This creates excessive imports, but it should be fine
+                additionalImports.add("$pkg.$sourceName")
+                additionalImports.add("$pkg.$outputName")
+            }
+        }
+
+        if (targets.size <= 0) return false
+        for ((_, sourceType, sourcePkg, sourceName, outputName) in targets.values) {
+            // Find all methods in this type and all super types to that need to be implemented
+            val types = ArrayDeque<TypeMirror>().apply { addLast(sourceType.asType()) }
+            val impAttrs = mutableListOf<GeneratedImport>()
+            val methods = mutableListOf<ExecutableElement>()
+            while (types.size > 0) {
+                val typeMirror = types.removeLast()
+                if (typeMirror.toString() == "java.lang.Object") continue
+                val type = procEnv.typeUtils.asElement(typeMirror)
+                for (member in type.enclosedElements) {
+                    if (member.kind != ElementKind.METHOD) continue
+                    methods.add(member as ExecutableElement)
+                }
+
+                impAttrs.addAll(type.getAnnotationsByType(GeneratedImport::class.java))
+                types.addAll(procEnv.typeUtils.directSupertypes(typeMirror))
+            }
+
+            val file = procEnv.filer.createSourceFile("$outputName")
+            TabbedWriter.writeTo(file.openWriter()) {
+                line("package $sourcePkg;")
+                line()
+
+                // Imports used by the proxy implementation
+                line("import android.util.Log;")
+                line("import java.lang.LinkageError;")
+                line("import com.android.systemui.plugins.PluginWrapper;")
+                line("import com.android.systemui.plugins.ProtectedPluginListener;")
+                line()
+
+                // Imports of other generated types
+                if (additionalImports.size > 0) {
+                    for (impTarget in additionalImports) {
+                        line("import $impTarget;")
+                    }
+                    line()
+                }
+
+                // Imports declared via @GeneratedImport
+                if (impAttrs.size > 0) {
+                    for (impAttr in impAttrs) {
+                        line("import ${impAttr.extraImport};")
+                    }
+                    line()
+                }
+
+                val interfaces = "$sourceName, PluginWrapper<$sourceName>"
+                braceBlock("public class $outputName implements $interfaces") {
+                    line("private static final String CLASS = \"$sourceName\";")
+
+                    // Static factory method to prevent wrapping the same object twice
+                    parenBlock("public static $outputName protect") {
+                        line("$sourceName instance,")
+                        line("ProtectedPluginListener listener")
+                    }
+                    braceBlock {
+                        line("if (instance instanceof $outputName)")
+                        line("    return ($outputName)instance;")
+                        line("return new $outputName(instance, listener);")
+                    }
+                    line()
+
+                    // Member Fields
+                    line("private $sourceName mInstance;")
+                    line("private ProtectedPluginListener mListener;")
+                    line("private boolean mHasError = false;")
+                    line()
+
+                    // Constructor
+                    parenBlock("private $outputName") {
+                        line("$sourceName instance,")
+                        line("ProtectedPluginListener listener")
+                    }
+                    braceBlock {
+                        line("mInstance = instance;")
+                        line("mListener = listener;")
+                    }
+                    line()
+
+                    // Wrapped instance getter for version checker
+                    braceBlock("public $sourceName getPlugin()") { line("return mInstance;") }
+
+                    // Method implementations
+                    for (method in methods) {
+                        val methodName = method.simpleName
+                        val returnTypeName = method.returnType.toString()
+                        val callArgs = StringBuilder()
+                        var isFirst = true
+
+                        line("@Override")
+                        parenBlock("public $returnTypeName $methodName") {
+                            // While copying the method signature for the proxy type, we also
+                            // accumulate arguments for the nested callsite.
+                            for (param in method.parameters) {
+                                if (!isFirst) completeLine(",")
+                                startLine("${param.asType()} ${param.simpleName}")
+                                isFirst = false
+
+                                if (callArgs.length > 0) callArgs.append(", ")
+                                callArgs.append(param.simpleName)
+                            }
+                        }
+
+                        val isVoid = method.returnType.kind == TypeKind.VOID
+                        val nestedCall = "mInstance.$methodName($callArgs)"
+                        val callStatement =
+                            when {
+                                isVoid -> "$nestedCall;"
+                                targets.containsKey(returnTypeName) -> {
+                                    val targetType = targets.get(returnTypeName)!!.outputName
+                                    "return $targetType.protect($nestedCall, mListener);"
+                                }
+                                else -> "return $nestedCall;"
+                            }
+
+                        // Simple property methods forgo protection
+                        val simpleAttr = method.getAnnotation(SimpleProperty::class.java)
+                        if (simpleAttr != null) {
+                            braceBlock {
+                                line("final String METHOD = \"$methodName\";")
+                                line(callStatement)
+                            }
+                            line()
+                            continue
+                        }
+
+                        // Standard implementation wraps nested call in try-catch
+                        braceBlock {
+                            val retAttr = method.getAnnotation(ProtectedReturn::class.java)
+                            val errorStatement =
+                                when {
+                                    retAttr != null -> retAttr.statement
+                                    isVoid -> "return;"
+                                    else -> {
+                                        // Non-void methods must be annotated.
+                                        procEnv.messager.printMessage(
+                                            Kind.ERROR,
+                                            "$outputName.$methodName must be annotated with " +
+                                                "@ProtectedReturn or @SimpleProperty",
+                                        )
+                                        "throw ex;"
+                                    }
+                                }
+
+                            line("final String METHOD = \"$methodName\";")
+
+                            // Return immediately if any previous call has failed.
+                            braceBlock("if (mHasError)") { line(errorStatement) }
+
+                            // Protect callsite in try/catch block
+                            braceBlock("try") { line(callStatement) }
+
+                            // Notify listener when a LinkageError is caught
+                            braceBlock("catch (LinkageError ex)") {
+                                line("Log.wtf(CLASS, \"Failed to execute: \" + METHOD, ex);")
+                                line("mHasError = mListener.onFail(CLASS, METHOD, ex);")
+                                line(errorStatement)
+                            }
+                        }
+                        line()
+                    }
+                }
+            }
+        }
+
+        // Write a centralized static factory type to its own file. This is for convience so that
+        // PluginInstance need not resolve each generated type at runtime as plugins are loaded.
+        val factoryFile = procEnv.filer.createSourceFile("PluginProtector")
+        TabbedWriter.writeTo(factoryFile.openWriter()) {
+            line("package com.android.systemui.plugins;")
+            line()
+
+            line("import java.util.Map;")
+            line("import java.util.ArrayList;")
+            line("import java.util.HashSet;")
+            line("import android.util.Log;")
+            line("import static java.util.Map.entry;")
+            line()
+
+            for (impTarget in additionalImports) {
+                line("import $impTarget;")
+            }
+            line()
+
+            braceBlock("public final class PluginProtector") {
+                line("private PluginProtector() { }")
+                line()
+
+                line("private static final String TAG = \"PluginProtector\";")
+                line()
+
+                // Untyped factory SAM, private to this type.
+                braceBlock("private interface Factory") {
+                    line("Object create(Object plugin, ProtectedPluginListener listener);")
+                }
+                line()
+
+                // Store a reference to each `protect` method in a map by interface type.
+                parenBlock("private static final Map<Class, Factory> sFactories = Map.ofEntries") {
+                    var isFirst = true
+                    for (target in targets.values) {
+                        if (!isFirst) completeLine(",")
+                        target.apply {
+                            startLine("entry($sourceName.class, ")
+                            appendLine("(p, h) -> $outputName.protect(($sourceName)p, h))")
+                        }
+                        isFirst = false
+                    }
+                }
+                completeLine(";")
+                line()
+
+                // Lookup the relevant factory based on the instance type, if not found return null.
+                parenBlock("public static <T> T tryProtect") {
+                    line("T target,")
+                    line("ProtectedPluginListener listener")
+                }
+                braceBlock {
+                    // Accumulate interfaces from type and all base types
+                    line("HashSet<Class> interfaces = new HashSet<Class>();")
+                    line("Class current = target.getClass();")
+                    braceBlock("while (current != null)") {
+                        braceBlock("for (Class cls : current.getInterfaces())") {
+                            line("interfaces.add(cls);")
+                        }
+                        line("current = current.getSuperclass();")
+                    }
+                    line()
+
+                    // Check if any of the interfaces are marked protectable
+                    line("int candidateCount = 0;")
+                    line("Factory candidateFactory = null;")
+                    braceBlock("for (Class cls : interfaces)") {
+                        line("Factory factory = sFactories.get(cls);")
+                        braceBlock("if (factory != null)") {
+                            line("candidateFactory = factory;")
+                            line("candidateCount++;")
+                        }
+                    }
+                    line()
+
+                    // No match, return null
+                    braceBlock("if (candidateFactory == null)") {
+                        line("Log.i(TAG, \"Wasn't able to wrap \" + target);")
+                        line("return null;")
+                    }
+
+                    // Multiple matches, not supported
+                    braceBlock("if (candidateCount >= 2)") {
+                        var error = "Plugin implements more than one protected interface"
+                        line("throw new UnsupportedOperationException(\"$error\");")
+                    }
+
+                    // Call the factory and wrap the target object
+                    line("return (T)candidateFactory.create(target, listener);")
+                }
+                line()
+
+                // Wraps the target with the appropriate generated proxy if it exists.
+                parenBlock("public static <T> T protectIfAble") {
+                    line("T target,")
+                    line("ProtectedPluginListener listener")
+                }
+                braceBlock {
+                    line("T result = tryProtect(target, listener);")
+                    line("return result != null ? result : target;")
+                }
+                line()
+            }
+        }
+
+        return true
+    }
+}
diff --git a/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/TabbedWriter.kt b/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/TabbedWriter.kt
new file mode 100644
index 0000000..941b2c2
--- /dev/null
+++ b/packages/SystemUI/plugin_core/processor/src/com/android/systemui/plugins/processor/TabbedWriter.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.plugins.processor
+
+import java.io.BufferedWriter
+import java.io.Writer
+
+/**
+ * [TabbedWriter] is a convience class which tracks and writes correctly tabbed lines for generating
+ * source files. These files don't need to be correctly tabbed as they're ephemeral and not part of
+ * the source tree, but correct tabbing makes debugging much easier when the build fails.
+ */
+class TabbedWriter(writer: Writer) : AutoCloseable {
+    private val target = BufferedWriter(writer)
+    private var isInProgress = false
+    var tabCount: Int = 0
+        private set
+
+    override fun close() = target.close()
+
+    fun line() {
+        target.newLine()
+        isInProgress = false
+    }
+
+    fun line(str: String) {
+        if (isInProgress) {
+            target.newLine()
+        }
+
+        target.append("    ".repeat(tabCount))
+        target.append(str)
+        target.newLine()
+        isInProgress = false
+    }
+
+    fun completeLine(str: String) {
+        if (!isInProgress) {
+            target.newLine()
+            target.append("    ".repeat(tabCount))
+        }
+
+        target.append(str)
+        target.newLine()
+        isInProgress = false
+    }
+
+    fun startLine(str: String) {
+        if (isInProgress) {
+            target.newLine()
+        }
+
+        target.append("    ".repeat(tabCount))
+        target.append(str)
+        isInProgress = true
+    }
+
+    fun appendLine(str: String) {
+        if (!isInProgress) {
+            target.append("    ".repeat(tabCount))
+        }
+
+        target.append(str)
+        isInProgress = true
+    }
+
+    fun braceBlock(str: String = "", write: TabbedWriter.() -> Unit) {
+        block(str, " {", "}", true, write)
+    }
+
+    fun parenBlock(str: String = "", write: TabbedWriter.() -> Unit) {
+        block(str, "(", ")", false, write)
+    }
+
+    private fun block(
+        str: String,
+        start: String,
+        end: String,
+        newLineForEnd: Boolean,
+        write: TabbedWriter.() -> Unit,
+    ) {
+        appendLine(str)
+        completeLine(start)
+
+        tabCount++
+        this.write()
+        tabCount--
+
+        if (newLineForEnd) {
+            line(end)
+        } else {
+            startLine(end)
+        }
+    }
+
+    companion object {
+        fun writeTo(writer: Writer, write: TabbedWriter.() -> Unit) {
+            TabbedWriter(writer).use { it.write() }
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/Plugin.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/Plugin.java
index 8ff6c11..84040f9 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/Plugin.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/Plugin.java
@@ -15,6 +15,7 @@
 
 import android.content.Context;
 
+import com.android.systemui.plugins.annotations.ProtectedReturn;
 import com.android.systemui.plugins.annotations.Requires;
 
 /**
@@ -116,6 +117,8 @@
      * @deprecated
      * @see Requires
      */
+    @Deprecated
+    @ProtectedReturn(statement = "return -1;")
     default int getVersion() {
         // Default of -1 indicates the plugin supports the new Requires model.
         return -1;
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginWrapper.kt b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginWrapper.kt
new file mode 100644
index 0000000..debb318
--- /dev/null
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginWrapper.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.plugins
+
+/** [PluginWrapper] wraps an interface used by a plugin */
+interface PluginWrapper<T> {
+    /** Instance that is being wrapped */
+    fun getPlugin(): T
+}
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/ProtectedPluginListener.kt b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/ProtectedPluginListener.kt
new file mode 100644
index 0000000..3a1f251
--- /dev/null
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/ProtectedPluginListener.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.plugins
+
+/** Listener for events from proxy types generated by [ProtectedPluginProcessor]. */
+interface ProtectedPluginListener {
+    /**
+     * Called when a method call produces a [LinkageError] before returning. This callback is
+     * provided so that the host application can terminate the plugin or log the error as
+     * appropriate.
+     *
+     * @return true to terminate all methods within this object; false if the error is recoverable
+     *   and the proxied plugin should continue to operate as normal.
+     */
+    fun onFail(className: String, methodName: String, failure: LinkageError): Boolean
+}
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/annotations/ProtectedInterface.kt b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/annotations/ProtectedInterface.kt
new file mode 100644
index 0000000..12a977d
--- /dev/null
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/annotations/ProtectedInterface.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.plugins.annotations
+
+/**
+ * This annotation marks denotes that an interface should use a proxy layer to protect the plugin
+ * host from crashing due to [LinkageError]s originating within the plugin's implementation.
+ */
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+annotation class ProtectedInterface
+
+/**
+ * This annotation specifies any additional imports that the processor will require when generating
+ * the proxy implementation for the target interface. The interface in question must still be
+ * annotated with [ProtectedInterface].
+ */
+@Repeatable
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+annotation class GeneratedImport(val extraImport: String)
+
+/**
+ * This annotation provides default values to return when the proxy implementation catches a
+ * [LinkageError]. The string specified should be a simple but valid java statement. In most cases
+ * it should be a return statement of the appropriate type, but in some cases throwing a known
+ * exception type may be preferred.
+ *
+ * This annotation is not required for methods that return void, but will behave the same way.
+ */
+@Target(
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY,
+    AnnotationTarget.PROPERTY_GETTER,
+    AnnotationTarget.PROPERTY_SETTER,
+)
+@Retention(AnnotationRetention.BINARY)
+annotation class ProtectedReturn(val statement: String)
+
+/**
+ * Some very simple properties and methods need not be protected by the proxy implementation. This
+ * annotation can be used to omit the normal try-catch wrapper the proxy is using. These members
+ * will instead be a direct passthrough.
+ *
+ * It should only be used for members where the plugin implementation is expected to be exceedingly
+ * simple. Any member marked with this annotation should be no more complex than kotlin's automatic
+ * properties, and make no other method calls whatsoever.
+ */
+@Target(
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY,
+    AnnotationTarget.PROPERTY_GETTER,
+    AnnotationTarget.PROPERTY_SETTER,
+)
+@Retention(AnnotationRetention.BINARY)
+annotation class SimpleProperty
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index 84f7a51..6c8db91 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -28,8 +28,4 @@
     <!-- Overload default clock widget parameters -->
     <dimen name="widget_big_font_size">100dp</dimen>
     <dimen name="widget_label_font_size">18sp</dimen>
-
-    <!-- New keyboard shortcut helper -->
-    <dimen name="shortcut_helper_width">704dp</dimen>
-    <dimen name="shortcut_helper_height">1208dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml b/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
deleted file mode 100644
index a15532f..0000000
--- a/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/shortcut_helper_sheet_container"
-    android:layout_gravity="center_horizontal|bottom"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <FrameLayout
-        android:id="@+id/shortcut_helper_sheet"
-        style="@style/ShortcutHelperBottomSheet"
-        android:layout_width="@dimen/shortcut_helper_width"
-        android:layout_height="@dimen/shortcut_helper_height"
-        android:orientation="vertical"
-        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
-
-        <!-- Drag handle for accessibility -->
-        <com.google.android.material.bottomsheet.BottomSheetDragHandleView
-            android:id="@+id/drag_handle"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-        <androidx.compose.ui.platform.ComposeView
-            android:id="@+id/shortcut_helper_compose_container"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" />
-    </FrameLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 91cd019..3b3ed39 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -215,4 +215,17 @@
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintVertical_bias="1.0"
         tools:srcCompat="@tools:sample/avatars" />
+
+    <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+        android:id="@+id/biometric_icon_overlay"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_gravity="center"
+        android:contentDescription="@null"
+        android:scaleType="fitXY"
+        android:importantForAccessibility="no"
+        app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
+        app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+        app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+        app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
index 51117a7..2a00495 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
@@ -40,6 +40,19 @@
         app:layout_constraintTop_toTopOf="parent"
         tools:srcCompat="@tools:sample/avatars" />
 
+    <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
+        android:id="@+id/biometric_icon_overlay"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_gravity="center"
+        android:contentDescription="@null"
+        android:scaleType="fitXY"
+        android:importantForAccessibility="no"
+        app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
+        app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
+        app:layout_constraintStart_toStartOf="@+id/biometric_icon"
+        app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
+
     <ScrollView
         android:id="@+id/scrollView"
         android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 32bcca1..1f4dea9 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -63,10 +63,12 @@
             <!-- Container that is wrapped around the views on the start half of the status bar.
                  Its width will change with the number of visible children and sub-children.
                  It is useful when we want to know the visible bounds of the content. -->
+            <!-- IMPORTANT: The height of this view *must* be match_parent so that the activity
+                 chips don't get cropped when they appear. See b/302160300 and b/366988057. -->
             <FrameLayout
                 android:id="@+id/status_bar_start_side_content"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:layout_gravity="center_vertical|start"
                 android:clipChildren="false">
 
@@ -75,6 +77,8 @@
                 <!-- The alpha of the start side is controlled by PhoneStatusBarTransitions, and the
                      individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK
                      and DISABLE_NOTIFICATION_ICONS, respectively -->
+                <!-- IMPORTANT: The height of this view *must* be match_parent so that the activity
+                 chips don't get cropped when they appear. See b/302160300 and b/366988057. -->
                 <LinearLayout
                     android:id="@+id/status_bar_start_side_except_heads_up"
                     android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index 856ba92..e26b855 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -15,7 +15,7 @@
   -->
 
 <!-- Extends Framelayout -->
-<com.android.systemui.statusbar.EmptyShadeView
+<com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -26,6 +26,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="center"
             android:gravity="center"
+            android:paddingHorizontal="8dp"
             >
         <TextView
                 android:id="@+id/no_notifications"
@@ -46,4 +47,4 @@
                 android:textAppearance="?android:attr/textAppearanceButton"
                 android:text="@string/unlock_to_see_notif_text"/>
     </LinearLayout>
-</com.android.systemui.statusbar.EmptyShadeView>
+</com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index fbb07be..22d34eb 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -58,14 +58,6 @@
              android:layout_height="match_parent"
              android:visibility="invisible" />
 
-    <!-- Root for all keyguard content. It was previously located within the shade. -->
-    <com.android.systemui.keyguard.ui.view.KeyguardRootView
-        android:id="@id/keyguard_root_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clipChildren="false"
-        />
-
     <!-- Shared container for the notification stack. Can be positioned by either
          the keyguard_root_view or notification_panel -->
     <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
@@ -76,6 +68,14 @@
         android:clipToPadding="false"
         />
 
+    <!-- Root for all keyguard content. It was previously located within the shade. -->
+    <com.android.systemui.keyguard.ui.view.KeyguardRootView
+        android:id="@id/keyguard_root_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        />
+
     <include layout="@layout/brightness_mirror_container" />
 
     <com.android.systemui.scrim.ScrimView
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 900c11e..d7c1359 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Luidsprekers en skerms"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde toestelle"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop jou gedeelde sessie om media na ’n ander toestel toe te skuif"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitsaai werk"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Voeg by"</string>
     <string name="manage_users" msgid="1823875311934643849">"Bestuur gebruikers"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Sleep na verdeelde skerm word nie vir hierdie kennisgewing gesteun nie"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Ligging is aktief"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑fi onbeskikbaar"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioriteitmodus"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wekker gestel"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Pasmaak sluitskerm"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Ontsluit om sluitskerm te pasmaak"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-fi is nie beskikbaar nie"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Ligging is aktief"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera is geblokkeer"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera en mikrofoon is geblokkeer"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofoon is geblokkeer"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeer met jou sleutelbord"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer kortpadsleutels"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeer met jou raakpaneel"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer raakpaneelgebare, kortpadsleutels en meer"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Teruggebaar"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Tuisgebaar"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Bekyk onlangse apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gaan terug"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Swiep enige plek op die raakpaneel links of regs met drie vingers om terug te gaan.\n\nJy kan ook die kortpadsleutelhandeling + Esc hiervoor gebruik."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Swiep enige tyd van die onderkant van jou skerm af op met drie vingers om na jou tuisskerm toe te gaan."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Mooi so!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Jy het die Gaan na Tuisskerm-gebaar voltooi."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Bekyk onlangse apps"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Swiep op en hou met drie vingers op jou raakpaneel."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Knap gedaan!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jy het die Bekyk Onlangse Apps-gebaar voltooi."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Handelingsleutel"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Druk die handelingsleutel op jou sleutelbord om toegang tot jou apps te kry."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Geluk!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swiep op en hou met drie vingers. Tik om meer gebare te leer."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gebruik jou sleutelbord om alle apps te bekyk"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Druk enige tyd die handelingsleutel. Tik om meer gebare te leer."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Ekstra donker is nou deel van die helderheidglyer"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Jy kan nou die skerm ekstra donker maak deur die helderheidvlak nog laer te maak.\n\nAangesien hierdie kenmerk nou deel van die helderheidglyer is, word kortpaaie vir ekstra donker verwyder."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Verwyder kortpaaie vir ekstra donker"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Kortpaaie vir ekstra donker is verwyder"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Konnektiwiteit"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Toeganklikheid"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Nutsdienste"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 6a68dd9..388601b 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ድምፅ ማውጫዎች እና ማሳያዎች"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"የተጠቆሙ መሣሪያዎች"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ግብዓት"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"ውጤት"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"ሚዲያን ወደ ሌላ መሣሪያ ለማንቀሳቀስ የተጋራውን ክፍለ ጊዜዎን ያቁሙ"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"አቁም"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ማሰራጨት እንዴት እንደሚሠራ"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"አክል"</string>
     <string name="manage_users" msgid="1823875311934643849">"ተጠቃሚዎችን ያስተዳድሩ"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ይህ ማሳወቂያ ወደ የተከፈለ ማያ ገፅ መጎተትን አይደግፍም"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"አካባቢ ገቢር ነው"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi አይገኝም"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"የቅድሚያ ሁነታ"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ማንቂያ ተቀናብሯል"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ማያ ገፅ ቁልፍን አብጅ"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"የማያ ገጽ ቁልፍን ለማበጀት ይክፈቱ"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi አይገኝም"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"አካባቢ ገቢር ነው"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ካሜራ ታግዷል"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ካሜራ እና ማይክሮፎን ታግደዋል"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ማይክሮፎን ታግዷል"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"የቁልፍ ሰሌዳዎን በመጠቀም ያስሱ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"የቁልፍ ሰሌዳ አቋራጮችን ይወቁ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"የመዳሰሻ ሰሌዳዎን በመጠቀም ያስሱ"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"የመዳሰሻ ሰሌዳ ምልክቶችን፣ የቁልፍ ሰሌዳ አቋራጮችን እና ሌሎችን ይወቁ"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"የተመለስ ምልክት"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"የቤት ምልክት"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ተከናውኗል"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ወደኋላ ተመለስ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ወደኋላ ለመመለስ የመዳሰሻ ሰሌዳው ላይ የትኛውም ቦታ በሦስት ጣቶች ወደግራ ወይም ወደቀኝ ያንሸራትቱ።\n\nእንዲሁም የቁልፍ ሰሌዳ አቋራጭ + ESC ለዚህ መጠቀም ይችላሉ።"</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"በማንኛውም ጊዜ ወደ መነሻ ማያ ገፅዎ ለመሄድ ከማያ ገፅዎ ታች በሦስት ጣቶች ወደላይ ያሸብልሉ።"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"አሪፍ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ወደ መነሻ ሂድ ምልክትን አጠናቅቀዋል።"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"የመዳሰሻ ሰሌዳዎ ላይ ሦስት ጣቶችን በመጠቀም ወደ ላይ ያንሸራትቱ እና ይያዙ።"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ጥሩ ሠርተዋል!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"የቅርብ ጊዜ መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል።"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"የተግባር ቁልፍ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"መተግበሪያዎችዎን ለመድረስ በቁልፍ ሰሌዳዎ ላይ የእርምጃ ቁልፉን ይጫኑ።"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"እንኳን ደስ አለዎት!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ እና ይያዙ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ሁሉንም መተግበሪያዎች ለማየት የቁልፍ ሰሌዳዎን ይጠቀሙ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"በማንኛውም ጊዜ የተግባር ቁልፍን ይጫኑ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ተጨማሪ ደብዛዛ አሁን የብሩህነት ተንሸራታች ክፍል ነው"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"አሁን ከማያ ገፅዎ በላይ የብሩህነት ደረጃውን ይበልጥ በመቀነስ ማያ ገፁን ተጨማሪ ደብዛዛ ማድረግ ይችላሉ።\n\nይህ ባህሪ አሁን የብሩህነት ተንሸራታች አካል ስለሆነ ተጨማሪ ደብዛዛ አቋራጮች እየተወገዱ ነው።"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"ተጨማሪ ደብዛዛ አቋራጮችን ያስወግዱ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"ተጨማሪ ደብዛዛ አቋራጮች ተወግደዋል"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"ግንኙነት"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ተደራሽነት"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"መገልገያዎች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 1273221..53b1dfb 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"مكبّرات الصوت والشاشات"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"الأجهزة المقترَحة"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"الإدخال"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"الإخراج"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"أوقِف الجلسة المشتركة لنقل الوسائط إلى جهاز آخر."</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"إيقاف"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"كيفية عمل البث"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"إضافة"</string>
     <string name="manage_users" msgid="1823875311934643849">"إدارة المستخدمين"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"لا يتيح هذا الإشعار إمكانية السحب لتقسيم الشاشة."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"الموقع الجغرافي نشط"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"‏شبكة Wi‑Fi غير متاحة"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"وضع الأولوية"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"تم ضبط المنبه."</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"تخصيص شاشة القفل"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"الفتح لتخصيص شاشة القفل"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"‏لا يتوفّر اتصال Wi-Fi."</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"الموقع الجغرافي نشط"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"استخدام الكاميرا محظور."</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"استخدام الكاميرا والميكروفون محظور."</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"استخدام الميكروفون محظور."</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"التنقّل باستخدام لوحة المفاتيح"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"تعرَّف على اختصارات لوحة المفاتيح"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"التنقّل باستخدام لوحة اللمس"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"تعرَّف على إيماءات لوحة اللمس واختصارات لوحة المفاتيح والمزيد"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"إيماءة الرجوع"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"إيماءة الانتقال إلى الشاشة الرئيسية"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"عرض التطبيقات المستخدَمة مؤخرًا"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تم"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"رجوع"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"‏للرجوع، مرِّر سريعًا لليمين أو لليسار باستخدام ثلاثة أصابع في أي مكان على لوحة اللمس.\n\nيمكنك أيضًا الرجوع باستخدام اختصار لوحة المفاتيح \"مفتاح الإجراء + ESC\"."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"للانتقال إلى الشاشة الرئيسية في أي وقت، مرِّر سريعًا من أسفل الشاشة إلى أعلاها بثلاثة أصابع."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"أحسنت"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"لقد أكملت التدريب على إيماءة الانتقال إلى الشاشة الرئيسية."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"عرض التطبيقات المستخدَمة مؤخرًا"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"مرِّر سريعًا للأعلى مع استمرار الضغط باستخدام ثلاثة أصابع على لوحة اللمس."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"أحسنت."</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"لقد أكملْت الدليل التوجيهي على إيماءة \"عرض التطبيقات المستخدَمة مؤخرًا\"."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"مفتاح الإجراء"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"للوصول إلى التطبيقات، اضغط على مفتاح الإجراء في لوحة المفاتيح."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"تهانينا!"</string>
@@ -1446,17 +1443,13 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"مرِّر سريعًا للأعلى مع استمرار الضغط باستخدام 3 أصابع. انقر للتعرّف على المزيد من الإيماءات."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"استخدِم لوحة المفاتيح لعرض جميع التطبيقات"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"اضغط على مفتاح الإجراء في أي وقت. انقر للتعرّف على المزيد من الإيماءات."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"أصبحت ميزة \"زيادة تعتيم الشاشة\" الآن جزءًا من شريط تمرير مستوى السطوع"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"يمكنك الآن زيادة تعتيم الشاشة عن طريق خفض مستوى السطوع بشكل أكبر.\n\nبما أنّ هذه الميزة أصبحت الآن جزءًا من شريط تمرير مستوى السطوع، يمكنك الآن إزالة اختصارات \"زيادة تعتيم الشاشة\"."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"إزالة اختصارات \"زيادة تعتيم الشاشة\""</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"تمّت إزالة اختصارات \"زيادة تعتيم الشاشة\""</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"إمكانية الاتصال"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"تسهيل الاستخدام"</string>
-    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"برامج الخدمات"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"خدمات عامة"</string>
     <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"الخصوصية"</string>
     <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"مقدَّمة من التطبيقات"</string>
     <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"العرض"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 76431a0..04cb99c 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"স্পীকাৰ আৰু ডিছপ্লে’"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"পৰামৰ্শ হিচাপে পোৱা ডিভাইচ"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ইনপুট"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"আউটপুট"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"মিডিয়া অন্য ডিভাইচলৈ স্থানান্তৰ কৰিবলৈ আপোনাৰ শ্বেয়াৰ কৰা ছেশ্বনটো বন্ধ কৰক"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"বন্ধ কৰক"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"সম্প্ৰচাৰ কৰাটোৱে কেনেকৈ কাম কৰে"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"যোগ দিয়ক"</string>
     <string name="manage_users" msgid="1823875311934643849">"পৰিচালনা কৰক"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"এই জাননীটোৱে বিভাজিত স্ক্ৰীনলৈ টানি আনি এৰাৰ সুবিধাটো সমৰ্থন নকৰে"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"অৱস্থান সক্ৰিয় হৈ আছে"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"ৱাই-ফাই উপলব্ধ নহয়"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"অগ্ৰাধিকাৰ ম’ড"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"এলাৰ্ম ছেট কৰা হ’ল"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"লক স্ক্ৰীন কাষ্টমাইজ কৰক"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"লক স্ক্ৰীন কাষ্টমাইজ কৰিবলৈ আনলক কৰক"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"ৱাই-ফাই উপলব্ধ নহয়"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"অৱস্থান সক্ৰিয় হৈ আছে"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"কেমেৰা অৱৰোধ কৰা আছে"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"কেমেৰা আৰু মাইক্ৰ’ফ’ন অৱৰোধ কৰা আছে"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"মাইক্ৰ’ফ’ন অৱৰোধ কৰা আছে"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"কীব’ৰ্ড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীব’ৰ্ডৰ শ্বৰ্টকাটসমূহৰ বিষয়ে জানক"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচ্চপেডৰ নিৰ্দেশ, কীব’ৰ্ডৰ শ্বৰ্টকাট আৰু অধিকৰ বিষয়ে জানক"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"উভতি যাওক নিৰ্দেশ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশ"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"শেহতীয়া এপ্‌সমূহ চাওক"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হ’ল"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"উভতি যাওক"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"উভতি যাবলৈ, টাচ্চপেডৰ যিকোনো স্থানত তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁ বা সোঁফালে ছোৱাইপ কৰক।\n\nইয়াৰ বাবে আপুনি কীব’ৰ্ড শ্বৰ্টকাট কাৰ্য + ESC ব্যৱহাৰ কৰিবও পাৰে।"</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"যিকোনো সময়তে আপোনাৰ গৃহ স্ক্ৰীনলৈ যাবলৈ, আপোনাৰ স্ক্ৰীনখনৰ একেবাৰে তলৰ পৰা ওপৰলৈ তিনিটা আঙুলিৰে ছোৱাইপ কৰক।"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"সুন্দৰ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"আপুনি গৃহ স্ক্ৰীনলৈ উভতি যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"শেহতীয়া এপ্‌সমূহ চাওক"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"আপোনাৰ টাচ্চপেডত তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি কিছু সময় ধৰি ৰাখক।"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"বঢ়িয়া!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"আপুনি শেহতীয়া এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে।"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"কাৰ্য কী"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"আপোনাৰ এপ্‌সমূহ এক্সেছ কৰিবলৈ আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক।"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"অভিনন্দন!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"আটাইবোৰ এপ্‌ চাবলৈ আপোনাৰ কীব’ৰ্ড ব্যৱহাৰ কৰক"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"যিকোনো সময়তে কাৰ্য কীটোত টিপক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"এক্সট্ৰা ডিম এতিয়া উজ্জ্বলতা শ্লাইডাৰৰ অংশ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"আপুনি এতিয়া উজ্জ্বলতাৰ স্তৰ আৰু অধিক হ্ৰাস কৰি স্ক্ৰীনখন এক্সট্ৰা ডিম কৰিব পাৰে।\n\nযিহেতু এতিয়া এই সুবিধাটো উজ্জ্বলতাৰ শ্লাইডাৰৰ অংশ, এক্সট্ৰা ডিমৰ শ্বৰ্টকাট আঁতৰাই থকা হৈছে।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"এক্সট্ৰা ডিমৰ শ্বৰ্টকাট আঁতৰাওক"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"এক্সট্ৰা ডিম শ্বৰ্টকাট আঁতৰোৱা হৈছে"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"সংযোগ"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"সাধ্য সুবিধা"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"সহায়ক সঁজুলি"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7c33d3b..85aa42f 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Dinamiklər &amp; Displeylər"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Təklif olunan Cihazlar"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Medianı başqa cihaza köçürmək üçün paylaşılan sessiyanı dayandırın"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Dayandırın"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayım necə işləyir"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Əlavə edin"</string>
     <string name="manage_users" msgid="1823875311934643849">"İstifadəçiləri idarə edin"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Bu bildiriş bölünmüş ekrana sürüşdürməyi dəstəkləmir"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Məkan aktivdir"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi əlçatan deyil"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioritet rejimi"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Siqnal ayarlanıb"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Kilid ekranını fərdiləşdirin"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Kilid ekranını fərdiləşdirmək üçün kiliddən çıxarın"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi əlçatan deyil"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Məkan aktivdir"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera bloklanıb"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera və mikrofon bloklanıb"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon bloklanıb"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviaturadan istifadə edərək hərəkət edin"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klaviatura qısayolları haqqında öyrənin"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Taçpeddən istifadə edərək hərəkət edin"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Taçped jestləri, klaviatura qısayolları və s. haqqında öyrənin"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geri jesti"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Əsas ekran jesti"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son tətbiqlərə baxın"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hazırdır"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Geri getmək üçün taçpeddə istənilən yerdə üç barmaqla sola və ya sağa çəkin.\n\nBunun üçün Action + ESC klaviatura qısayolundan da istifadə edə bilərsiniz."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"İstənilən vaxt ana ekrana keçmək üçün ekranın aşağısından üç barmağınızla yuxarı çəkin."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Əla!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Əsas ekrana keçid jestini tamamladınız."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Son tətbiqlərə baxın"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Taçpeddə üç barmaq ilə yuxarı çəkib saxlayın."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Əla!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son tətbiqlərə baxmaq jestini tamamladınız."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Fəaliyyət açarı"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Tətbiqlərə daxil olmaq üçün klaviaturada fəaliyyət açarını basın."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Təbriklər!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Üç barmaq ilə yuxarı çəkib saxlayın. Daha çox jest öyrənmək üçün toxunun."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Bütün tətbiqlərə baxmaq üçün klaviatura istifadə edin"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"İstənilən vaxt fəaliyyət açarını basın. Daha çox jest öyrənmək üçün toxunun."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Əlavə qaraltma artıq parlaqlıq slayderinin bir hissəsidir"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"İndi ekranın yuxarısında parlaqlıq səviyyəsini daha da aşağı salaraq ekranı əlavə qaralda bilərsiniz.\n\nBu funksiya artıq parlaqlıq slayderinin bir hissəsi olduğundan əlavə qaraltma qısayolları silinir."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Əlavə qaraltma qısayollarını silin"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Əlavə qaraltma qısayolları silindi"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Bağlantı"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Xüsusi imkanlar"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Kommunal xidmətlər"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index bc6037a..b838f7c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Ulaz"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Izlaz"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Zaustavite deljenu sesiju da biste premestili medijski sadržaj na drugi uređaj"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Zaustavi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcioniše emitovanje"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Dodaj"</string>
     <string name="manage_users" msgid="1823875311934643849">"Upravljaj korisnicima"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Ovo obaveštenje ne podržava prevlačenje na podeljeni ekran"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokacija je aktivna"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"WiFi nije dostupan"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioritetni režim"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je podešen"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Prilagodi zaključani ekran"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Otključajte da biste prilagodili zaključani ekran"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"WiFi nije dostupan"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokacija je aktivna"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera je blokirana"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera i mikrofon su blokirani"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon je blokiran"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tasterskim prečicama"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću tačpeda"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučite pokrete za tačped, tasterske prečice i drugo"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za vraćanje"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za početnu stranicu"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavno korišćene aplikacije"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Da biste se vratili, prevucite ulevo sa tri prsta bilo gde na tačpedu.\n\nMožete da koristite i tastersku prečicu Alt + ESC za ovo."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Da biste otišli na početni ekran u bilo kom trenutku, prevucite nagore od dna ekrana pomoću tri prsta."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Svaka čast!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Dovršili ste pokret za povratak na početnu stranicu."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaži nedavno korišćene aplikacije"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Prevucite nagore i zadržite pomoću tri prsta na tačpedu."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Odlično!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dovršili ste pokret za prikazivanje nedavno korišćenih aplikacija."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Taster radnji"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Da biste pristupili aplikacijama, pritisnite taster radnji na tastaturi."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite sa tri prsta. Dodirnite da biste videli više pokreta."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Koristite tastaturu da biste pregledali sve aplikacije"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite taster radnji u bilo kom trenutku. Dodirnite da biste videli više pokreta."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Dodatno zatamnjivanje je sada deo klizača za osvetljenost"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Sada možete dodatno da zatamnite ekran smanjivanjem nivoa osvetljenosti. \n\nOva funkcija je sada deo klizača za osvetljenost, pa se prečice za dodatno zatamnjivanje uklanjaju."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Ukloni prečice za dodatno zatamnjivanje"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Uklonjene su prečice za dodatno zatamnjivanje"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Povezivanje"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Pristupačnost"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Uslužne aplikacije"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 1f0c127..038732c 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Дынамікі і дысплэі"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Прылады, якія падтрымліваюцца"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Каб перамясціць медыяфайлы на іншую прыладу, спыніце абагулены сеанс"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Спыніць"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як адбываецца трансляцыя"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Дадаць"</string>
     <string name="manage_users" msgid="1823875311934643849">"Кіраванне карыстальнікамі"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Гэта апавяшчэнне нельга перацягнуць на падзелены экран."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Геаданыя ўключаны"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Сетка Wi‑Fi недаступная"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Прыярытэтны рэжым"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будзільнік зададзены"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Наладзіць экран блакіроўкі"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Разблакіруйце, каб наладзіць экран блакіроўкі"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Сетка Wi-Fi недаступная"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Геаданыя ўключаны"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камера заблакіравана"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камера і мікрафон заблакіраваны"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Мікрафон заблакіраваны"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігацыя з дапамогай клавіятуры"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Азнаёмцеся са спалучэннямі клавіш"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігацыя з дапамогай сэнсарнай панэлі"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Азнаёмцеся з жэстамі для сэнсарнай панэлі, спалучэннямі клавіш і г. д."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жэст для вяртання на папярэдні экран"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жэст для вяртання на галоўны экран"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прагляд нядаўніх праграм"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Гатова"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Каб вярнуцца, правядзіце трыма пальцамі ўлева ці ўправа ў любым месцы сэнсарнай панэлі.\n\nТаксама можна выкарыстоўваць спалучэнне \"клавіша дзеяння + ESC\"."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Каб у любы момант перайсці на галоўны экран, правядзіце па экране трыма пальцамі знізу ўверх."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Выдатна!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Вы навучыліся рабіць жэст для пераходу на галоўны экран."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прагляд нядаўніх праграм"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Правядзіце па сэнсарнай панэлі трыма пальцамі ўверх і затрымайце пальцы."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Выдатная праца!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы скончылі вывучэнне жэсту для прагляду нядаўніх праграм."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавіша дзеяння"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Каб атрымаць доступ да праграм, націсніце клавішу дзеяння на клавіятуры."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Віншуем!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Правядзіце трыма пальцамі ўверх і затрымайце пальцы. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Выкарыстайце клавіятуру для прагляду ўсіх праграм"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Можна націснуць на клавішу дзеяння ў любы момант. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Цяпер кіраваць дадатковым памяншэннем яркасці можна з дапамогай паўзунка яркасці"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Цяпер вы можаце дадаткова зацямніць экран, яшчэ больш панізіўшы ўзровень яркасці.\n\nПаколькі гэта функцыя цяпер уваходзіць у склад паўзунка яркасці, хуткія каманды для дадатковага памяншэння яркасці былі выдалены."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Выдаліць хуткія каманды для дадатковага памяншэння яркасці"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Хуткія каманды для дадатковага памяншэння яркасці выдалены"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Магчымасць падключэння"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Спецыяльныя магчымасці"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Утыліты"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index affe260..17a8ed5 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Високоговорители и екрани"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени устройства"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Спиране на споделената ви сесия с цел преместване на мултимедията на друго устройство"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Спиране"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работи предаването"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Добавяне"</string>
     <string name="manage_users" msgid="1823875311934643849">"Потребители"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Това известие не поддържа плъзгане за разделяне на екрана"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Местоположението е активно"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi не е налице"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Приоритетен режим"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будилникът е зададен"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Персонализ. на заключения екран"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Отключете, за да персонализирате заключения екран"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi не е налице"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Местоположението е активно"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Достъпът до камерата е блокиран"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Достъпът до камерата и микрофона е блокиран"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Достъпът до микрофона е блокиран"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигирайте посредством клавиатурата си"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете за клавишните комбинации"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигирайте посредством сензорния панел"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете за жестовете със сензорния панел, клавишните комбинации и др."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест за връщане назад"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест за преминаване към началния екран"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Преглед на скорошните приложения"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"За да се върнете назад, прекарайте три пръста наляво или надясно по сензорния панел.\n\nЗа целта можете също да използвате комбинацията с клавиша за действия + ESC."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"За да преминете към началния екран по всяко време, прекарайте три пръста нагоре от долната част на екрана."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Чудесно!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Изпълнихте жеста за преминаване към началния екран."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Преглед на скорошните приложения"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Плъзнете нагоре с три пръста по сензорния панел и задръжте."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Изпълнихте жеста за преглед на скорошните приложения."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавиш за действия"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"За да осъществите достъп до приложенията, натиснете клавиша за действия на клавиатурата си."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Поздравления!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Плъзнете нагоре с три пръста и задръжте. Докоснете, за да научите повече жестове."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Използвайте клавиатурата, за да прегледате всички приложения"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Натиснете клавиша за действия по всяко време. Докоснете, за да научите повече жестове."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Функцията за допълнително затъмняване вече е част от плъзгача за яркостта"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Вече можете да затъмнявате екрана допълнително, като намалите яркостта още повече.\n\nТъй като тази функция вече е част от плъзгача за яркостта, преките пътища за допълнително затъмняване се премахват."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Премахване на преките пътища за допълнително затъмняване"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Преките пътища за допълнително затъмняване са премахнати"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Свързаност"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Достъпност"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Помощни услуги"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 497730c..ebec652 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"স্পিকার ও ডিসপ্লে"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"সাজেস্ট করা ডিভাইস"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"অন্য ডিভাইসে মিডিয়া সরাতে আপনার শেয়ার করা সেশন বন্ধ করুন"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"বন্ধ করুন"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ব্রডকাস্ট কীভাবে কাজ করে"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"যোগ করুন"</string>
     <string name="manage_users" msgid="1823875311934643849">"ব্যবহারকারীদের ম্যানেজ করুন"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"\'স্প্লিটস্ক্রিন\' মোডে এই বিজ্ঞপ্তি টেনে আনা যাবে না"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"লোকেশন অ্যাক্টিভ আছে"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"ওয়াই-ফাই উপলভ্য নেই"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"প্রায়োরিটি মোড"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"অ্যালার্ম সেট করা হয়েছে"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"লক স্ক্রিন কাস্টমাইজ করুন"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"লক স্ক্রিন কাস্টমাইজ করতে আনলক করুন"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"ওয়াই-ফাই উপলভ্য নয়"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"লোকেশন অ্যাক্টিভ আছে"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ক্যামেরার অ্যাক্সেস ব্লক করা আছে"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ক্যামেরা এবং মাইক্রোফোনের অ্যাক্সেস ব্লক করা আছে"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"মাইক্রোফোনের অ্যাক্সেস ব্লক করা আছে"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"আপনার কীবোর্ড ব্যবহার করে নেভিগেট করুন"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীবোর্ড শর্টকাট সম্পর্কে জানুন"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপনার টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচপ্যাড জেসচার, কীবোর্ড শর্টকাট এবং আরও অনেক কিছু সম্পর্কে জানুন"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ফিরে যাওয়ার জেসচার"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"হোমপেজে যাওয়ার জেসচার"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হয়ে গেছে"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ফিরে যান"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ফিরে যেতে, টাচপ্যাডে যেকোনও জায়গায় তিনটি আঙুল দিয়ে বাঁদিক বা ডানদিকে সোয়াইপ করুন।\n\nএছাড়া, এটির জন্য আপনি কীবোর্ড শর্টকাট অ্যাকশন + ESC বোতাম প্রেস করতে পারবেন।"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"যেকোনও সময়ে আপনার হোম স্ক্রিনে যেতে, আপনার স্ক্রিনের একদম নিচের থেকে তিনটি আঙুল দিয়ে উপরের দিকে সোয়াইপ করুন।"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"সাবাস!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"জেসচার ব্যবহার করে কীভাবে হোমে ফিরে যাওয়া যায় সেই সম্পর্কে আপনি জেনেছেন।"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপ দেখুন"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"আপনার টাচপ্যাডে তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন।"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"অসাধারণ!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"সম্প্রতি ব্যবহার করা হয়েছে এমন অ্যাপের জেসচার দেখা সম্পূর্ণ করেছেন।"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"অ্যাকশন কী"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"আপনার অ্যাপ অ্যাক্সেস করতে, কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"অভিনন্দন!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"সব অ্যাপ দেখতে আপনার কীবোর্ড ব্যবহার করুন"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"যেকোনও সময় অ্যাকশন কী প্রেস করুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচার এখন ব্রাইটনেস স্লাইডারের একটি অংশ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"আপনি ব্রাইটনেস লেভেল কমিয়েও, স্ক্রিন অতিরিক্ত কম ব্রাইট করতে পারবেন।\n\nএই ফিচার এখন ব্রাইটনেস স্লাইডারের অংশ, তাই \'অতিরিক্ত কম ব্রাইটনেস\' ফিচারের শর্টকাট সরানো হচ্ছে।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচারের শর্টকাট সরান"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচারের শর্টকাট সরানো হয়েছে"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"কানেক্টিভিটি"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"অ্যাক্সেসিবিলিটি"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"উপযোগিতা"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 509bf6f..34e3d80 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i ekrani"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Ulaz"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Izlaz"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Zaustavite dijeljenu sesiju da premjestite medij na drugi uređaj"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Zaustavi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako funkcionira emitiranje"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Dodaj"</string>
     <string name="manage_users" msgid="1823875311934643849">"Upravljajte korisnicima"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Ovo obavještenje ne podržava prevlačenje na podijeljeni ekran"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokacija je aktivna"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"WiFi je nedostupan"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Način rada Prioriteti"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je postavljen"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Prilagodi zaključani ekran"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Otključajte da prilagodite zaključani ekran"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"WiFi mreža nije dostupna"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokacija je aktivna"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera je blokirana"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera i mikrofon su blokirani"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon je blokiran"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o prečicama tastature"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima na dodirnoj podlozi, prečicama tastature i drugim opcijama"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za povratak"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za povratak na početni ekran"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavne aplikacije"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Da se vratite, prevucite ulijevo ili udesno s tri prsta bilo gdje na dodirnoj podlozi.\n\nZa ovo možete koristiti i radnju za prečicu i Esc na tastaturi."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Da odete na početni ekran bilo kada, prevucite s dna ekrana nagore s tri prsta."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Lijepo!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Savladali ste pokret za odlazak na početni ekran."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaz nedavnih aplikacija"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Prevucite nagore i zadržite s tri prsta na dodirnoj podlozi."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavnih aplikacija."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tipka radnji"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Da pristupite aplikacijama, pritisnite tipku radnji na tastaturi."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite s tri prsta. Dodirnite da saznate za više pokreta."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Koristite tastaturu da pregledate sve aplikacije"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku radnji bilo kada. Dodirnite da saznate za više pokreta."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Dodatno zatamnjenje je sada dio klizača za osvijetljenost"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Sada možete dodatno zatamniti ekran daljnjim smanjenjem nivoa osvijetljenosti.\n\nBudući da je ova funkcija sada dio klizača za osvijetljenost, prečice za dodatno zatamnjenje se uklanjaju."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Ukloni prečice za dodatno zatamnjenje"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Prečice za dodatno zatamnjenje su uklonjene"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Povezivost"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Pristupačnost"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Uslužni programi"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8c9c597..27f704d 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altaveus i pantalles"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositius suggerits"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Sortida"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Atura la sessió compartida per moure contingut multimèdia a un altre dispositiu"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Atura"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Com funciona l\'emissió"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Afegeix"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gestiona usuaris"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Aquesta notificació no es pot arrossegar a la pantalla dividida"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Ubicació activa"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi no disponible"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Mode Prioritat"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma definida"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalitza pantalla de bloqueig"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Desbloqueja per personalitzar la pantalla de bloqueig"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"No hi ha cap Wi‑Fi disponible"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Ubicació activa"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"La càmera està bloquejada"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"La càmera i el micròfon estan bloquejats"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"El micròfon està bloquejat"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega amb el teclat"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprèn les tecles de drecera"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega amb el ratolí tàctil"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprèn els gestos del ratolí tàctil, les tecles de drecera i més"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gest Enrere"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gest Inici"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Mostra les aplicacions recents"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Per tornar enrere, llisca cap a l\'esquerra o cap a la dreta amb tres dits en qualsevol lloc del ratolí tàctil.\n\nTambé pots utilitzar les tecles d\'accions de drecera+Esc."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Per anar a la pantalla d\'inici en qualsevol moment, fes lliscar tres dits cap amunt des de la part inferior de la pantalla."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Molt bé!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Has completat el gest per anar a la pantalla d\'inici."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Mostra les aplicacions recents"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Llisca cap amunt amb tres dits i mantén premut al ratolí tàctil."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ben fet!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completat el gest per veure les aplicacions recents."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla d\'acció"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Per accedir a les aplicacions, prem la tecla d\'acció al teclat."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Enhorabona!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Llisca cap amunt amb tres dits i mantén premut. Toca per aprendre més gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utilitza el teclat per veure totes les aplicacions"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Prem la tecla d\'acció en qualsevol moment. Toca per aprendre més gestos."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"L\'atenuació extra ara forma part del control lliscant de brillantor"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Ara pots atenuar encara més la pantalla abaixant-ne el nivell de brillantor.\n\nCom que aquesta funció ara forma part del control lliscant de brillantor, les dreceres d\'atenuació extra se suprimiran."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Suprimeix les dreceres d\'atenuació extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"S\'han suprimit les dreceres d\'atenuació extra"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivitat"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibilitat"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitats"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 6d2328e..c824837 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -318,11 +318,11 @@
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Naslouchátka"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapínání…"</string>
-    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčení"</string>
+    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. otáčení"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčení obrazovky"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
-    <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Spořič obrazovky"</string>
-    <string name="quick_settings_camera_label" msgid="5612076679385269339">"Přístup k fotoaparátu"</string>
+    <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Spořič"</string>
+    <string name="quick_settings_camera_label" msgid="5612076679385269339">"Fotoaparát"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"Přístup k mikrofonu"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupné"</string>
     <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"Blokováno"</string>
@@ -442,7 +442,7 @@
     <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuto"</string>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavit"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Spravovat v nastavení"</string>
-    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Žádné aktivní režimy}=1{Režim {mode} je aktivní}few{# režimy jsou aktivní}many{# režimu je aktivních}other{# režimů je aktivních}}"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Žádné aktivní}=1{Režim {mode} je aktivní}few{# režimy jsou aktivní}many{# režimu je aktivních}other{# režimů je aktivních}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků, upozornění, událostí a volajících, které zadáte. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Přizpůsobit"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a displeje"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Navrhovaná zařízení"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Ukončí sdílenou relaci a bude možné přesunout médium do jiného zařízení"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Zastavit"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak vysílání funguje"</string>
@@ -1234,7 +1238,7 @@
     <string name="person_available" msgid="2318599327472755472">"Dostupné"</string>
     <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problém s načtením měřiče baterie"</string>
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Klepnutím zobrazíte další informace"</string>
-    <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Budík nenastaven"</string>
+    <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Žádný"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"zadejte zámek obrazovky"</string>
     <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dotkněte se snímače otisků prstů. Vypínač je kratší tlačítko na boku telefonu."</string>
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Snímač otisků prstů"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Přidat"</string>
     <string name="manage_users" msgid="1823875311934643849">"Spravovat uživatele"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Toto oznámení nepodporuje přetažení na rozdělenou obrazovku"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Určování polohy aktivní"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Síť Wi‑Fi není k dispozici"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioritní režim"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Je nastaven budík"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Přizpůsobit obrazovku uzamčení"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Obrazovku uzamčení upravíte, když zařízení odemknete"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Síť Wi-Fi není dostupná"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Určování polohy aktivní"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera je blokována"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera a mikrofon jsou blokovány"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon je blokován"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigujte pomocí klávesnice"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte se klávesové zkratky"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigujte pomocí touchpadu"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte se gesta touchpadu, klávesové zkratky a další"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto zpět"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto domů"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazit nedávné aplikace"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pokud se chcete vrátit zpět, stačí kdekoli na touchpadu přejet třemi prsty doleva nebo doprava.\n\nMůžete také použít klávesovou zkratku Akce + ESC."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Na plochu přejdete kdykoli přejetím třemi prsty ze spodní části obrazovky nahoru."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Skvělé!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Dokončili jste gesto pro přechod na plochu."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazit nedávné aplikace"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Přejeďte po touchpadu třemi prsty nahoru a podržte je."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Výborně!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Provedli jste gesto pro zobrazení nedávných aplikací."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Akční klávesa"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Přístup k aplikacím získáte stisknutím akční klávesy na klávesnici."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulujeme!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Přejeďte třemi prsty nahoru a podržte je. Další gesta zjistíte klepnutím."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Zobrazení všech aplikací pomocí klávesnice"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Kdykoli stiskněte akční klávesu. Další gesta zjistíte klepnutím."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Na posuvníku jasu lze nově nastavit velmi tmavou obrazovku"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Obrazovku můžete nastavit jako velmi tmavou tím, že ještě víc snížíte jas.\n\nVzhledem k tomu, že je tato funkce nyní součástí posuvníku jasu, zkratky pro velmi tmavou obrazovku budou odstraněny."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Odstranit zkratky pro velmi tmavou obrazovku"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Zkratky pro velmi tmavou obrazovku byly odstraněny"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Připojení"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Přístupnost"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Nástroje"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 261cf33..feac02d 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Højttalere og skærme"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåede enheder"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop din delte session for at flytte medier til en anden enhed"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Sådan fungerer udsendelser"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Tilføj"</string>
     <string name="manage_users" msgid="1823875311934643849">"Administrer brugere"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Denne notifikation kan ikke trækkes til en opdelt skærm"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokation er aktiv"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Ingen tilgængelig Wi-Fi-forbindelse"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Tilstanden Prioritet"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmen er indstillet"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Tilpas låseskærm"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Lås op for at tilpasse låseskærmen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi er ikke tilgængeligt"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokation er aktiv"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kameraet er blokeret"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Der er blokeret for kameraet og mikrofonen"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofonen er blokeret"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger ved hjælp af dit tastatur"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Se tastaturgenveje"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger ved hjælp af din touchplade"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Se bevægelser på touchpladen, tastaturgenveje m.m."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Bevægelse for at gå tilbage"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Bevægelse for at gå til startskærm"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se seneste apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Udfør"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Du kan gå tilbage ved at stryge mod venstre eller højre med tre fingre et vilkårligt sted på touchpladen.\n\nDu kan også bruge tastaturgenvejen Alt + Esc."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Du kan til enhver tid stryge opad med tre fingre fra bunden af skærmen, hvis du vil gå til startskærmen."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Sådan!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Du har fuldført bevægelsen for Gå til startskærmen."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Se seneste apps"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Stryg opad, og hold tre fingre på touchpladen."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Godt klaret!"</string>
+    <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="tutorial_action_key_title" msgid="2659466586996495447">"Handlingstast"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Du kan tilgå alle dine apps ved at trykke på handlingstasten på dit tastatur."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tillykke!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Stryg opad, og hold tre fingre nede. Tryk for at lære flere bevægelser."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Brug dit tastatur til at se alle apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tryk på handlingstasten når som helst. Tryk for at lære flere bevægelser."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Ekstra dæmpet belysning er nu en del af skyderen til lysstyrke"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Du kan nu dæmpe skærmens belysning ekstra meget ved at reducere lysstyrken endnu mere.\n\nDa denne funktion nu er en del af skyderen til lysstyrke, fjernes genveje til ekstra dæmpet belysning."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Fjern genveje til ekstra dæmpet belysning"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Genveje til ekstra dæmpet belysning er fjernet"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Forbindelse"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Hjælpefunktioner"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Værktøjer"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index f0e1871c..3235c3a 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -576,8 +576,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Jetzt starten"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Keine Benachrichtigungen"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Keine neuen Benachrichtigungen"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Verringern von Lautstärke und Vibration bei Benachrichtigungen ist an"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Wenn du in kurzer Zeit zu viele Benachrichtigungen erhältst, wird automatisch für bis zu 2 Minuten die Lautstärke des Geräts verringert und Benachrichtigungen werden reduziert."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"„Benachrichtigungen reduzieren” ist aktiviert"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Wenn du zu viele Benachrichtigungen auf einmal erhältst, wird die Lautstärke automatisch bis zu 2 min lang verringert und Benachrichtigungen werden minimiert."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktivieren"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Für ältere Benachrichtigungen entsperren"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dieses Gerät wird von deinen Eltern verwaltet"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Lautsprecher &amp; Displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vorgeschlagene Geräte"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Beende die Freigabesitzung zur Übertragung von Medien auf das andere Gerät"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Beenden"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Funktionsweise von Nachrichten an alle"</string>
@@ -1396,6 +1400,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigation mit der Tastatur"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informationen zu Tastenkombinationen"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigation mit dem Touchpad"</string>
@@ -1454,18 +1460,11 @@
     <skip />
     <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
     <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Konnektivität"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Bedienungshilfen"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Dienstprogramme"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Datenschutz"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Von Apps bereitgestellt"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unbekannt"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 16082cb..2187a17 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -576,7 +576,7 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Δεν υπάρχουν ειδοποιήσεις"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Δεν υπάρχουν νέες ειδοποιήσεις"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Η περίοδος cooldown ειδοποιήσεων είναι ενεργή"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Η ρύθμιση cooldown ειδοποιήσεων είναι ενεργή"</string>
     <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Αυτόματη μείωση έντασης ήχου συσκευής και ειδοποιήσεων για έως 2 λεπτά όταν λαμβάνετε πολλές ειδοποιήσεις ταυτόχρονα."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Απενεργοποίηση"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ξεκλειδώστε για εμφάνιση παλαιότ. ειδοπ."</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Ηχεία και οθόνες"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Προτεινόμενες συσκευές"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Διακόψτε την κοινόχρηστη περίοδο λειτουργίας για να μεταφέρετε μέσα σε άλλη συσκευή"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Διακοπή"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Πώς λειτουργεί η μετάδοση"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Προσθήκη"</string>
     <string name="manage_users" msgid="1823875311934643849">"Διαχ. χρηστών"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Αυτή η ειδοποίηση δεν υποστηρίζει τη μεταφορά με σύρσιμο για τον διαχωρισμό οθόνης"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Τοποθεσία ενεργή"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Το Wi‑Fi δεν είναι διαθέσιμο"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Λειτουργία προτεραιότητας"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Το ξυπνητήρι ρυθμίστηκε"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Προσαρμογή οθόνης κλειδώματος"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Ξεκλειδώστε για προσαρμογή της οθόνης κλειδώματος"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Δεν υπάρχει διαθέσιμο δίκτυο Wi-Fi"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Τοποθεσία ενεργή"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Η κάμερα έχει αποκλειστεί"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Η κάμερα και το μικρόφωνο έχουν αποκλειστεί"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Το μικρόφωνο έχει αποκλειστεί"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Πλοήγηση με το πληκτρολόγιο"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Μάθετε συντομεύσεις πληκτρολογίου"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Πλοήγηση με την επιφάνεια αφής"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Μάθετε κινήσεις επιφάνειας αφής, συντομεύσεις πληκτρολογίου και άλλα"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Κίνηση επιστροφής"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Κίνηση μετάβασης στην αρχική οθόνη"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Προβολή πρόσφατων εφαρμογών"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Τέλος"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Επιστροφή"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Για να επιστρέψετε, σύρετε προς τα αριστερά ή προς τα δεξιά χρησιμοποιώντας τρία δάχτυλα σε οποιοδήποτε σημείο της επιφάνειας αφής.\n\nΜπορείτε επίσης να χρησιμοποιήσετε τη συντόμευση πληκτρολογίου Action + ESC."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Για μετάβαση στην αρχική οθόνη ανά πάσα στιγμή, σύρετε προς τα επάνω με τρία δάχτυλα από το κάτω μέρος της οθόνης."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Ωραία!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Προβολή πρόσφατων εφαρμογών"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Σύρετε προς τα πάνω με τρία δάχτυλα στην επιφάνεια αφής και μην τα σηκώσετε."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Μπράβο!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ολοκληρώσατε την κίνηση για την προβολή πρόσφατων εφαρμογών."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Πλήκτρο ενέργειας"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Για να αποκτήσετε πρόσβαση στις εφαρμογές σας, πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Συγχαρητήρια!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Σύρετε προς τα πάνω με τρία δάχτυλα και μην τα σηκώσετε. Πατήστε για να δείτε περισσότερες κινήσεις."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Χρήση του πληκτρολογίου για προβολή όλων των εφαρμογών"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Πιέστε το πλήκτρο ενέργειας ανά πάσα στιγμή. Πατήστε για να μάθετε περισσότερες κινήσεις."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Η λειτουργία επιπλέον μείωσης φωτεινότητας είναι πλέον μέρος του ρυθμιστικού φωτεινότητας"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Τώρα μπορείτε να μειώσετε επιπλέον τη φωτεινότητα της οθόνης, χαμηλώνοντας το επίπεδο φωτεινότητας ακόμα περισσότερο.\n\nΕπειδή αυτή η λειτουργία είναι πλέον μέρος του ρυθμιστικού φωτεινότητας, οι συντομεύσεις για επιπλέον μείωση φωτεινότητας καταργήθηκαν."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Κατάργηση συντομεύσεων για επιπλέον μείωση φωτεινότητας"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Οι συντομεύσεις για επιπλέον μείωση φωτεινότητας καταργήθηκαν"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Συνδεσιμότητα"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Προσβασιμότητα"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Βοηθητικά προγράμματα"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e0bf0d6..3a961e2 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Output"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Add"</string>
     <string name="manage_users" msgid="1823875311934643849">"Manage users"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"This notification does not support dragging to split screen"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Location active"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi unavailable"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Priority mode"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Customise lock screen"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Unlock to customise lock screen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi not available"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Location active"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Camera is blocked"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Camera and microphone blocked"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microphone is blocked"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut Action + Esc for this."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"To go to your home screen at any time, swipe up with three fingers from the bottom of your screen."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Nice!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"You completed the go home gesture."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Swipe up and hold using three fingers on your touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"To access your apps, press the action key on your keyboard."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Congratulations!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Extra dim is now part of the brightness slider"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"You can now make the screen extra dim by lowering the brightness level even further.\n\nSince this feature is now part of the brightness slider, extra dim shortcuts are being removed."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Remove extra dim shortcuts"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Extra dim shortcuts removed"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivity"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibility"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilities"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 6f626e9..ba71ccf 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers &amp; Displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested Devices"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Output"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
@@ -1394,6 +1396,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e0bf0d6..3a961e2 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Output"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Add"</string>
     <string name="manage_users" msgid="1823875311934643849">"Manage users"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"This notification does not support dragging to split screen"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Location active"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi unavailable"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Priority mode"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Customise lock screen"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Unlock to customise lock screen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi not available"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Location active"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Camera is blocked"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Camera and microphone blocked"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microphone is blocked"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut Action + Esc for this."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"To go to your home screen at any time, swipe up with three fingers from the bottom of your screen."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Nice!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"You completed the go home gesture."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Swipe up and hold using three fingers on your touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"To access your apps, press the action key on your keyboard."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Congratulations!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Extra dim is now part of the brightness slider"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"You can now make the screen extra dim by lowering the brightness level even further.\n\nSince this feature is now part of the brightness slider, extra dim shortcuts are being removed."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Remove extra dim shortcuts"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Extra dim shortcuts removed"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivity"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibility"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilities"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e0bf0d6..3a961e2 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Output"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"How broadcasting works"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Add"</string>
     <string name="manage_users" msgid="1823875311934643849">"Manage users"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"This notification does not support dragging to split screen"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Location active"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi unavailable"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Priority mode"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Customise lock screen"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Unlock to customise lock screen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi not available"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Location active"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Camera is blocked"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Camera and microphone blocked"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microphone is blocked"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut Action + Esc for this."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"To go to your home screen at any time, swipe up with three fingers from the bottom of your screen."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Nice!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"You completed the go home gesture."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Swipe up and hold using three fingers on your touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"To access your apps, press the action key on your keyboard."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Congratulations!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Extra dim is now part of the brightness slider"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"You can now make the screen extra dim by lowering the brightness level even further.\n\nSince this feature is now part of the brightness slider, extra dim shortcuts are being removed."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Remove extra dim shortcuts"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Extra dim shortcuts removed"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivity"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibility"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilities"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 936b64d..eb76a7e 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>‎‏‎‎‏‏‏‎%%‎‏‎‎‏‎"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎Speakers &amp; Displays‎‏‎‎‏‎"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎Suggested Devices‎‏‎‎‏‎"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‎Input‎‏‎‎‏‎"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎Output‎‏‎‎‏‎"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎Stop your shared session to move media to another device‎‏‎‎‏‎"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‎‎Stop‎‏‎‎‏‎"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎How broadcasting works‎‏‎‎‏‎"</string>
@@ -1394,6 +1396,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎Collapse icon‎‏‎‎‏‎"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎Expand icon‎‏‎‎‏‎"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎or‎‏‎‎‏‎"</string>
+    <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎Drag handle‎‏‎‎‏‎"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎Navigate using your keyboard‎‏‎‎‏‎"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎Learn keyboards shortcuts‎‏‎‎‏‎"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎Navigate using your touchpad‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 96af732..b1d0c23 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bocinas y pantallas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Detiene la sesión de uso compartido para transferir contenido multimedia a otro dispositivo"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Detener"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la transmisión"</string>
@@ -1396,6 +1400,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega con el teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega con el panel táctil"</string>
@@ -1454,18 +1460,11 @@
     <skip />
     <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
     <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Conectividad"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accesibilidad"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilidades"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Privacidad"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por apps"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index fb8ceec..21b42ad 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altavoces y pantallas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Detén tu sesión compartida para transferir el contenido multimedia a otro dispositivo"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Detener"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Añadir"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gestionar usuarios"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Esta notificación no se puede arrastrar a la pantalla dividida"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Ubicación activa"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi no disponible"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modo prioritario"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma añadida"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizar pantalla de bloqueo"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Desbloquea para personalizar la pantalla de bloqueo"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Red Wi-Fi no disponible"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Ubicación activa"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Cámara bloqueada"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Cámara y micrófono bloqueados"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Micrófono bloqueado"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Desplázate con el teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Desplázate con el panel táctil"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende gestos del panel táctil, combinaciones de teclas y más"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto para volver"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para ir al inicio"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver aplicaciones recientes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hecho"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para volver, desliza con tres dedos hacia la izquierda o la derecha en cualquier parte del panel táctil.\n\nTambién puedes hacerlo con la combinación de teclas asignada + Esc."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para ir a la pantalla de inicio en cualquier momento, desliza hacia arriba con tres dedos desde la parte inferior de la pantalla."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"¡Muy bien!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Has completado el gesto para ir a la pantalla de inicio."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver aplicaciones recientes"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Desliza tres dedos hacia arriba y mantén pulsado en el panel táctil."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completado el gesto para ver las aplicaciones recientes."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de acción"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acceder a tus aplicaciones, pulsa la tecla de acción de tu teclado."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"¡Enhorabuena!"</string>
@@ -1446,26 +1445,15 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Desliza hacia arriba y mantén pulsado con tres dedos. Toca para aprender a usar más gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa el teclado para ver todas las aplicaciones"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pulsa la tecla de acción en cualquier momento. Toca para aprender a usar más gestos."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"La atenuación extra ahora forma parte del control deslizante de brillo"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Ahora puedes atenuar aún más tu pantalla reduciendo el nivel de brillo incluso más todavía.\n\nComo esta función ahora forma parte del control deslizante de brillo, se van a quitar los accesos directos de atenuación extra."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Eliminar accesos directos de atenuación extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Accesos directos de atenuación extra eliminados"</string>
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Conectividad"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accesibilidad"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilidades"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Privacidad"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por aplicaciones"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 430fcf0..c1807ad 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kõlarid ja ekraanid"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Soovitatud seadmed"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Peatage jagatud seanss, et meedia teise seadmesse teisaldada"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Peata"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kuidas ülekandmine toimib?"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Lisa"</string>
     <string name="manage_users" msgid="1823875311934643849">"Kasutajate haldamine"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"See märguanne ei toeta jagatud ekraanikuvale lohistamist."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Aktiivne koht"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"WiFi pole saadaval"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Režiim Prioriteetne"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm on määratud"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Kohanda lukustuskuva"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Lukustuskuva kohandamiseks avage"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"WiFi pole saadaval"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Aktiivne koht"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kaamera on blokeeritud"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kaamera ja mikrofon on blokeeritud"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon on blokeeritud"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeerige klaviatuuri abil"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Õppige klaviatuuri otseteid"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeerige puuteplaadi abil"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Õppige puuteplaadi liigutusi, klaviatuuri otseteid ja palju muud"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tagasiliikumisliigutus"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Avakuvale liikumise liigutus"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Hiljutiste rakenduste vaatamine"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Tagasiliikumiseks pühkige puuteplaadil kolme sõrmega vasakule või paremale.\n\nSamuti saate selle jaoks kasutada klaviatuuri otseteed toiminguklahv + paoklahv."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Mis tahes ajal avakuvale liikumiseks pühkige kolme sõrmega ekraanikuva allosast üles."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Hästi tehtud!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Tegite avakuvale minemise liigutuse."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Hiljutiste rakenduste vaatamine"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Pühkige üles ja hoidke kolme sõrmega puuteplaadil."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Väga hea!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Lõpetasite hiljutiste rakenduste vaatamise liigutuse."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Toiminguklahv"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Rakendustele juurdepääsemiseks vajutage klaviatuuril toiminguklahvi."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Õnnitleme!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pühkige kolme sõrmega üles ja hoidke sõrmi plaadil. Puudutage žestide kohta lisateabe saamiseks."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Klaviatuuri kasutamine kõigi rakenduste kuvamiseks"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Vajutage soovitud ajal toiminguklahvi. Puudutage žestide kohta lisateabe saamiseks."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Funktsioon „Eriti tume“ on nüüd osa ereduse liugurist"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Nüüd saate muuta ekraani eriti tumedaks, vähendades ereduse taset veelgi rohkem.\n\nKuna see funktsioon kuulub nüüd ereduse liugurisse, eemaldatakse funktsiooni „Eriti tume“ otseteed."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Eemalda funktsiooni „Eriti tume“ otseteed"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Funktsiooni „Eriti tume“ otseteed eemaldati"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Ühenduvus"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Juurdepääsetavus"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utiliidid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index ef6990c..50a55f9 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -442,7 +442,7 @@
     <string name="zen_mode_off" msgid="1736604456618147306">"Desaktibatuta"</string>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfiguratu"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Kudeatu ezarpenetan"</string>
-    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ez dago modurik aktibo}=1{{mode} aktibo dago}other{# modu aktibo daude}}"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ez dago modurik aktibo}=1{\"{mode}\" aktibo dago}other{# modu aktibo daude}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak, gertaera eta abisuen tonuak, eta aukeratzen dituzun deitzaileen dei-tonuak joko ditu. Bestalde, zuk erreproduzitutako guztia entzungo duzu, besteak beste, musika, bideoak eta jokoak."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak joko ditu. Hala ere, zuk erreproduzitutako guztia entzun ahal izango duzu, besteak beste, musika, bideoak eta jokoak."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Pertsonalizatu"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%% <xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Bozgorailuak eta pantailak"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Iradokitako gailuak"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Gelditu partekatutako saioa multimedia-edukia beste gailu batera eramateko"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Gelditu"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Nola funtzionatzen dute iragarpenek?"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Gehitu"</string>
     <string name="manage_users" msgid="1823875311934643849">"Kudeatu erabiltzaileak"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Jakinarazpen hau ezin da arrastatu pantaila zatitura"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Kokapena aktibatuta dago"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wifi-konexioa ez dago erabilgarri"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Lehentasun modua"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma ezarrita dago"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Pertsonalizatu pantaila blokeatua"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Desblokeatu eta pertsonalizatu pantaila blokeatua"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wifi-konexioa ez dago erabilgarri"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Kokapena aktibatuta dago"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera blokeatuta dago"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera eta mikrofonoa blokeatuta daude"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofonoa blokeatuta dago"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nabigatu teklatua erabilita"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ikasi lasterbideak"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nabigatu ukipen-panela erabilita"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Ikasi ukipen-paneleko keinuak, lasterbideak eta abar"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Atzera egiteko keinua"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Orri nagusira joateko keinua"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ikusi azkenaldiko aplikazioak"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Eginda"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Egin atzera"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Atzera egiteko, pasatu 3 hatz ezkerrera edo eskuinera ukipen-panelean.\n\nEkintza + Ihes lasterbidea ere erabil dezakezu horretarako."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Orri nagusira joateko, pasatu 3 hatz pantailaren behealdetik gora."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Ederki!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ikasi duzu hasierako pantailara joateko keinua."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ikusi azkenaldiko aplikazioak"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Pasatu 3 hatz gora eta eduki sakatuta ukipen-panelean."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bikain!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Osatu duzu azkenaldiko aplikazioak ikusteko keinua."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Ekintza-tekla"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Aplikazioak atzitzeko, sakatu teklatuko ekintza-tekla."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Zorionak!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasatu 3 hatz gora eta eduki sakatuta. Sakatu keinu gehiago ikasteko."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Erabili teklatua aplikazio guztiak ikusteko"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Sakatu ekintza-tekla edonoiz. Sakatu keinu gehiago ikasteko."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Orain, argitasunaren graduatzailean agertzen da Are ilunago"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Orain, pantaila are ilunago jar dezakezu, argitasun-maila are gehiago jaitsita.\n\nOrain eginbide hori argitasunaren graduatzailean agertzen denez, Are ilunago eginbidearen lasterbideak kendu egingo dira."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Kendu Are ilunago eginbidearen lasterbideak"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Kendu dira Are ilunago eginbidearen lasterbideak"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Konexioa"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Erabilerraztasuna"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Zerbitzu-aplikazioak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7239b9b..8f413c8 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"بلندگوها و نمایشگرها"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"دستگاه‌های پیشنهادی"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ورودی"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"برونداد"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"جلسه مشترک برای انتقال رسانه به دستگاهی دیگر متوقف می‌شود"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"توقف"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"همه‌فرتستی چطور کار می‌کند"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"افزودن"</string>
     <string name="manage_users" msgid="1823875311934643849">"مدیریت کاربران"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"این اعلان از عملکرد کشیدن به صفحهٔ دونیمه پشتیبانی نمی‌کند"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"مکان فعال است"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"‏Wi‑Fi دردسترس نیست"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"حالت اولویت"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"زنگ ساعت تنظیم شد"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"سفارشی‌سازی صفحه قفل"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"برای سفارشی‌سازی صفحه قفل، قفل را باز کنید"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"‏Wi-Fi دردسترس نیست"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"مکان فعال است"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"دوربین مسدود شده است"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"دوربین و میکروفون مسدود شده‌اند"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"میکروفون مسدود شده است"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"پیمایش کردن بااستفاده از صفحه‌کلید"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"آشنایی با میان‌برهای صفحه‌کلید"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"پیمایش کردن بااستفاده از صفحه لمسی"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"آشنایی با اشاره‌های صفحه لمسی، میان‌برهای صفحه‌کلید، و موارد دیگر"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"اشاره برگشت"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"اشاره صفحه اصلی"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"مشاهده کردن برنامه‌های اخیر"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تمام"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"برگشتن"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"برای برگشتن، در هر جایی از صفحه لمسی، با سه انگشت تند به‌چپ یا راست بکشید.\n\nبرای این کار می‌توانید از میان‌بر صفحه‌کلید «کنش + گریز» هم استفاده کنید."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"برای رفتن به صفحه اصلی در هرزمانی، با سه انگشت از پایین صفحه‌نمایش تند به‌بالا بکشید."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"آفرین!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"اشاره رفتن به صفحه اصلی را تکمیل کردید."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"مشاهده کردن برنامه‌های اخیر"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"با سه انگشت روی صفحه لمسی تند به‌بالا بکشید و نگه دارید."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"عالی است!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"اشاره مشاهده برنامه‌های اخیر را انجام دادید"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"دکمه کنش"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"برای دسترسی به برنامه‌هایتان، دکمه کنش در صفحه‌کلید را فشار دهید."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"تبریک!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"با سه انگشت تند به‌بالا بکشید و نگه دارید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"برای مشاهده همه برنامه‌ها، از صفحه‌کلید استفاده کنید"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"در هرزمانی دکمه کنش را فشار دهید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"«بسیار کم‌نور» اکنون بخشی از لغزنده روشنایی است"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"اکنون می‌توانید با پایین‌تر آوردن سطح روشنایی، صفحه‌نمایش را بسیار کم‌نور کنید.\n\nاز آنجایی‌که این ویژگی اکنون بخشی از لغزنده روشنایی است، میان‌برهای بسیار کم‌نور برداشته می‌شوند."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"برداشتن میان‌برهای بسیار کم‌نور"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"میان‌برهای بسیار کم‌نور برداشته شدند"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"اتصال‌پذیری"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"دسترس‌پذیری"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"برنامه‌های کمکی"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index d0133c7..f8fa57d 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Kaiuttimet ja näytöt"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ehdotetut laitteet"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Lopeta jaettu istunto, jotta voit siirtyä mediaan toisella laitteella"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Lopeta"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Miten lähetys toimii"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Lisää"</string>
     <string name="manage_users" msgid="1823875311934643849">"Ylläpidä käyttäjiä"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Ilmoitus ei tue jaetulle näytölle vetämistä"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Aktiivinen sijainti"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi ei ole saatavilla"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Tärkeät-tila"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Hälytys asetettu"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Muokkaa lukitusnäyttöä"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Voit muokata lukitusnäyttöä, kun avaat lukituksen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi-yhteys ei ole käytettävissä"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Aktiivinen sijainti"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera estetty"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera ja mikrofoni estetty"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofoni estetty"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Siirry käyttämällä näppäimistöä"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Opettele pikanäppäimiä"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Siirry käyttämällä kosketuslevyä"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Opettele kosketuslevyn eleitä, pikanäppäimiä ja muuta"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Takaisin-ele"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Etusivu-ele"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Katso viimeisimmät sovellukset"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Takaisin"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Jos haluat siirtyä takaisin, pyyhkäise kosketuslevyllä vasemmalle tai oikealle kolmella sormella.\n\nVoit myös käyttää pikanäppäinyhdistelmää toimintonäppäin + ESC."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Voit siirtyä aloitusnäytölle milloin tahansa pyyhkäisemällä ylös näytön alareunasta kolmella sormella."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Hienoa!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Olet oppinut aloitusnäytölle palaamiseleen."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Katso viimeisimmät sovellukset"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Hienoa!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Olet oppinut Katso viimeisimmät sovellukset ‑eleen."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Toimintonäppäin"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Voit käyttää sovelluksia painamalla näppäimistön toimintonäppäintä."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Onnittelut!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella. Lue lisää eleistä napauttamalla."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Kaikkien sovellusten näkeminen näppäimistön avulla"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Voit painaa toimintonäppäintä milloin tahansa. Lue lisää eleistä napauttamalla."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Erittäin himmeä on nyt osa kirkkauden liukusäädintä"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Voit nyt tehdä näytöstä erittäin himmeän vähentämällä kirkkautta entistäkin enemmän.\n\nKoska tämä ominaisuus on nyt osa kirkkauden liukusäädintä, erittäin himmeä ‑pikanäppäimet poistetaan."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Poista erittäin himmeä ‑pikanäppäimet"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Erittäin himmeä ‑pikanäppäimet poistettu"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Yhteydet"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Saavutettavuus"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Apusovellukset"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 95d7707..106af39f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"L\'atténuation des notifications est activée"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Le volume de votre appareil est réduit pendant deux minutes si vous recevez trop de notifications."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes maximum quand vous recevez trop de notifications à la fois."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverr. pour voir les anciennes notif."</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Cet appareil est géré par ton parent"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Haut-parleurs et écrans"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Arrêtez votre session partagée pour déplacer des contenus multimédias vers un autre appareil"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Arrêter"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement de la diffusion"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Ajouter"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gérer utilisateurs"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Cette notification ne prend pas en charge l\'écran partagé par glissement"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Position active"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi-Fi non disponible"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Mode priorité"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"L\'alarme a été réglée"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personn. l\'écran de verrouillage"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Déverrouiller pour personnaliser l\'écran de verrouillage"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi non accessible"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Position active"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Appareil photo bloqué"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Appareil photo et microphone bloqués"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microphone bloqué"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide de votre clavier"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis-clavier"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Apprenez les gestes du pavé tactile, les raccourcis-clavier et bien plus encore"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geste de retour"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Geste d\'accès à l\'écran d\'accueil"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pour revenir en arrière, balayez vers la gauche ou la droite en utilisant trois doigts n\'importe où sur le pavé tactile.\n\nVous pouvez également utiliser le raccourci clavier Action+Échap."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Pour accéder à votre écran d\'accueil à tout moment, balayez l\'écran du bas vers le haut avec trois doigts."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bien!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Vous avez appris le geste de retour à l\'écran d\'accueil."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Balayez l\'écran vers le haut avec trois doigts et maintenez-les en place sur votre pavé tactile."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bon travail!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez effectué le geste pour afficher les applis récentes."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Touche d\'action"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Pour accéder à vos applis, appuyez sur la touche d\'action de votre clavier."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Félicitations!"</string>
@@ -1446,26 +1445,15 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Balayez l\'écran vers le haut avec trois doigts et maintenez-les en place. Touchez pour apprendre d\'autres gestes."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utiliser votre clavier pour afficher toutes les applis"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Appuyez sur la touche d\'action à tout moment. Touchez pour apprendre d\'autres gestes."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"La réduction supplémentaire de la luminosité fait désormais partie du curseur de luminosité"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Vous pouvez désormais réduire la luminosité de l\'écran encore plus.\n\nÉtant donné que cette fonctionnalité fait désormais partie du curseur de luminosité, les raccourcis de la réduction supplémentaire de la luminosité sont retirés."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Retirer les raccourcis de la réduction supplémentaire de la luminosité"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Les raccourcis de la réduction supplémentaire de la luminosité ont été retirés"</string>
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivité"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibilité"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitaires"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Confidentialité"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournies par des applis"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Affichage"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index ee0f987..ad7a875 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Enceintes et écrans"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Appareils suggérés"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Arrêter votre session partagée pour déplacer des contenus multimédias vers un autre appareil"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Arrêter"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Ajouter"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gérer utilisateurs"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Impossible de faire glisser cette notification vers l\'écran partagé"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Position active"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi non disponible"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Mode Prioritaire"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme réglée"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personnaliser écran verrouillage"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Déverrouillez pour personnaliser l\'écran de verrouillage"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi non disponible"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Position active"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Caméra bloquée"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Caméra et micro bloqués"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Micro bloqué"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide du clavier"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Découvrir les raccourcis clavier"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Découvrir les gestes au pavé tactile, les raccourcis clavier et plus encore"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geste Retour"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Geste Accueil"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Afficher les applis récentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pour revenir en arrière, balayez vers la gauche ou vers la droite avec trois doigts n\'importe où sur le pavé tactile.\n\nVous pouvez aussi utiliser le raccourci clavier Action+Échap pour cela."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Pour accéder à l\'écran d\'accueil à tout moment, balayez l\'écran du bas vers le haut avec trois doigts."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bravo !"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Vous avez appris le geste pour revenir à l\'écran d\'accueil."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Afficher les applis récentes"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Avec trois doigts, balayez le pavé tactile vers le haut et maintenez la position."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bravo !"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Vous avez appris le geste pour afficher les applis récentes"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Touche d\'action"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Pour accéder à vos applis, appuyez sur la touche d\'action de votre clavier."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Félicitations !"</string>
@@ -1446,26 +1445,15 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Balayez vers le haut en utilisant trois doigts et maintenez. Appuyez pour apprendre d\'autres gestes."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utilisez votre clavier pour afficher toutes les applis"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Appuyez sur la touche d\'action à tout moment. Appuyez pour apprendre d\'autres gestes."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Luminosité ultra-réduite fait désormais partie du curseur de luminosité"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Vous pouvez désormais rendre l\'écran encore plus sombre en abaissant davantage le niveau de luminosité.\n\nComme cette fonctionnalité fait désormais partie du curseur de luminosité, les raccourcis de luminosité ultra-réduite sont supprimés."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Supprimer les raccourcis de luminosité ultra-réduite"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Raccourcis de luminosité ultra-réduite supprimés"</string>
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivité"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibilité"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitaires"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Confidentialité"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournis par des applis"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Écran"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9214095..bfe5b58 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Non hai notificacións"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Non hai notificacións novas"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"A opción Amainar notificacións está activada"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Cando recibas moitas notificacións, o volume e as alertas reduciranse automaticamente durante ata dous minutos."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Ao recibir moitas notificacións, o volume e as alertas redúcense automaticamente ata dous minutos."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver máis notificacións"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"O teu pai ou nai xestiona este dispositivo"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altofalantes e pantallas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos suxeridos"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Detén a sesión de uso compartido para mover contido multimedia a outro dispositivo"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Deter"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funcionan as difusións?"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Engadir"</string>
     <string name="manage_users" msgid="1823875311934643849">"Usuarios"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Esta notificación non pode arrastrarse á pantalla dividida"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Localización activa"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"A wifi non está dispoñible"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modo de prioridade"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma definida"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizar pantalla de bloqueo"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Para personalizar a pantalla de bloqueo, primeiro desbloquea o dispositivo"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wifi non dispoñible"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Localización activa"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"A cámara está bloqueada"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"A cámara e o micrófono están bloqueados"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"O micrófono está bloqueado"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega co teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende a usar os atallos de teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega co panel táctil"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende a usar os xestos do panel táctil, atallos de teclado e moito máis"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Xesto para volver"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Xesto para ir ao inicio"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Consultar aplicacións recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Feito"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para retroceder, pasa tres dedos cara á esquerda ou cara á dereita en calquera parte do panel táctil.\n\nTamén podes usar o atallo de teclado Acción + Escape."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para ir á pantalla de inicio, pasa tres dedos cara arriba desde a parte inferior da pantalla."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Excelente!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Completaches o xesto de ir ao inicio."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Consultar aplicacións recentes"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Pasa tres dedos cara arriba e mantenos premidos no panel táctil."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Moi ben!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaches o titorial do xesto de consultar aplicacións recentes."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de acción"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acceder ás aplicacións, preme a tecla de acción do teclado."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasa tres dedos cara arriba e mantenos premidos. Toca para obter máis información sobre os xestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa o teclado para ver todas as aplicacións"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Preme a tecla de acción cando queiras. Toca para obter máis información sobre os xestos."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"A atenuación extra agora está incluída no control desprazable do brillo"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Agora podes aumentar a atenuación da pantalla: só tes que baixar o nivel de brillo aínda máis.\n\nComo agora esta opción está incluída no control desprazable do brillo, quitaranse os atallos de atenuación extra."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Quitar atallos de atenuación extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Quitáronse os atallos de atenuación extra"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Conectividade"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accesibilidade"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilidades"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index f4de6ca..5d952ac 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"સ્પીકર અને ડિસ્પ્લે"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"સૂચવેલા ડિવાઇસ"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ઇનપુટ"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"આઉટપુટ"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"મીડિયાને બીજા ડિવાઇસ પર ખસેડવા માટે તમારું શેર કરેલું સત્ર રોકો"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"રોકો"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"બ્રોડકાસ્ટ પ્રક્રિયાની કામ કરવાની રીત"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"ઉમેરો"</string>
     <string name="manage_users" msgid="1823875311934643849">"વપરાશકર્તાઓને મેનેજ કરો"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"આ નોટિફિકેશન તેને વિભાજિત સ્ક્રીનમાં ખેંચવાની સુવિધાને સપોર્ટ કરતું નથી"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"લોકેશન સક્રિય"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"વાઇ-ફાઇ ઉપલબ્ધ નથી"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"પ્રાધાન્યતા મોડ"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"અલાર્મ સેટ"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"લૉક સ્ક્રીન કસ્ટમાઇઝ કરો"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"લૉક સ્ક્રીનને કસ્ટમાઇઝ કરવા માટે અનલૉક કરો"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"વાઇ-ફાઇ ઉપલબ્ધ નથી"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"લોકેશન સક્રિય"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"કૅમેરા બ્લૉક કરેલો છે"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"કૅમેરા અને માઇક્રોફોન બ્લૉક કરેલા છે"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"માઇક્રોફોન બ્લૉક કરેલો છે"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"તમારા કીબોર્ડ વડે નૅવિગેટ કરો"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"કીબોર્ડ શૉર્ટકર્ટ જાણો"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"તમારા ટચપૅડ વડે નૅવિગેટ કરો"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ટચપૅડના સંકેતો અને કીબોર્ડના શૉર્ટકટ જેવું બીજું ઘણું જાણો"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"પાછળ જવાનો સંકેત"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"હોમ સ્ક્રીન પર જવાનો સંકેત"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"તાજેતરની ઍપ જુઓ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"થઈ ગયું"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"પાછા જાઓ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"પાછા જવા માટે, ટચપૅડ પર ગમે ત્યાં ત્રણ આંગળી વડે ડાબે અથવા જમણે સ્વાઇપ કરો.\n\nઆના માટે તમે કીબોર્ડ શૉર્ટકટ Action + ESCનો ઉપયોગ કરી શકો છો."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"કોઈપણ સમયે તમારી હોમ સ્ક્રીન પર જવા માટે, ત્રણ આંગળી વડે તમારી સ્ક્રીનની સૌથી નીચેની બાજુએથી ઉપરની તરફ સ્વાઇપ કરો."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"સરસ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"તમે હોમ સ્ક્રીન પર જવાનો સંકેત પૂર્ણ કર્યો છે."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"તાજેતરની ઍપ જુઓ"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"તમારા ટચપૅડ પર ત્રણ આંગળીઓનો ઉપયોગ કરીને ઉપર સ્વાઇપ કરો અને દબાવી રાખો."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ખૂબ સરસ કામ!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"તમે \"તાજેતરની ઍપ જુઓ\" સંકેત પૂર્ણ કર્યો."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ઍક્શન કી"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"તમારી ઍપ ઍક્સેસ કરવા માટે, તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"અભિનંદન!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ત્રણ આંગળીઓનો ઉપયોગ કરીને ઉપર સ્વાઇપ કરો અને દબાવી રાખો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"બધી ઍપ જોવા માટે તમારા કીબોર્ડનો ઉપયોગ કરો"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"કોઈપણ સમયે ઍક્શન કી દબાવો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"બ્રાઇટનેસ સ્લાઇડર હવે એક્સ્ટ્રા ડિમનો ભાગ છે"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"તમે હવે બ્રાઇટનેસ લેવલને હજી પણ ઘટાડીને સ્ક્રીનને એક્સ્ટ્રા ડિમ બનાવી શકો છો.\n\nઆ સુવિધા હવે બ્રાઇટનેસ સ્લાઇડરનો ભાગ હોવાથી એક્સ્ટ્રા ડિમ શૉર્ટકટને કાઢી નાખવામાં આવી રહ્યાં છે."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"એક્સ્ટ્રા ડિમ શૉર્ટકટ કાઢી નાખો"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"એક્સ્ટ્રા ડિમ શૉર્ટકટ કાઢી નાખ્યા"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"કનેક્ટિવિટી"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ઍક્સેસિબિલિટી"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"યુટિલિટી"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index b9af773..03028b1 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पीकर और डिसप्ले"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"सुझाए गए डिवाइस"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"इनपुट"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"आउटपुट"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"मीडिया को किसी दूसरे डिवाइस में ट्रांसफ़र करने के लिए, अपने शेयर किए गए सेशन को बंद करें"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"बंद करें"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्ट करने की सुविधा कैसे काम करती है"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"जोड़ें"</string>
     <string name="manage_users" msgid="1823875311934643849">"उपयोगकर्ताओं को मैनेज करें"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"इस सूचना को स्प्लिट स्क्रीन मोड में, खींचा और छोड़ा नहीं जा सकता"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"जगह की जानकारी की सेटिंग चालू है"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"वाई-फ़ाई उपलब्ध नहीं है"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"प्राथमिकता मोड"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट किया गया"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"लॉक स्क्रीन को पसंद के मुताबिक बनाएं"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"लॉक स्क्रीन को पसंद के मुताबिक बनाने के लिए अनलॉक करें"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"वाई-फ़ाई उपलब्ध नहीं है"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"जगह की जानकारी की सेटिंग चालू है"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"कैमरे का ऐक्सेस नहीं है"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"कैमरे और माइक्रोफ़ोन का ऐक्सेस नहीं है"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"माइक्रोफ़ोन का ऐक्सेस नहीं है"</string>
@@ -1396,6 +1396,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
+    <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"कीबोर्ड का इस्तेमाल करके नेविगेट करें"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट के बारे में जानें"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचपैड का इस्तेमाल करके नेविगेट करें"</string>
@@ -1404,8 +1405,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपैड पर हाथ के जेस्चर, कीबोर्ड शॉर्टकट वगैरह के बारे में जानें"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"पीछे जाने का जेस्चर"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम स्क्रीन पर जाने का जेस्चर"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"हो गया"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"वापस जाएं"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"वापस जाने के लिए, टचपैड पर कहीं भी तीन उंगलियों से दाईं या बाईं ओर स्वाइप करें.\n\nइसके अलावा, ऐसा करने के लिए Action + ESC बटन का भी इस्तेमाल किया जा सकता है."</string>
@@ -1415,14 +1415,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"किसी भी समय फ़ोन की होम स्क्रीन पर जाने के लिए, तीन उंगलियों से फ़ोन पर सबसे नीचे से ऊपर की ओर स्वाइप करें."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"बढ़िया!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"आपने जान लिया कि हाथ का जेस्चर इस्तेमाल करके, होम स्क्रीन पर कैसे जाएं."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और फिर होल्ड करें."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"बहुत बढ़िया!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"आपने हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने के लिए, हाथ के जेस्चर के बारे में जान लिया है."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ऐक्शन बटन"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"अपने ऐप्लिकेशन ऐक्सेस करने के लिए, कीबोर्ड पर ऐक्शन बटन दबाएं."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"बधाई हो!"</string>
@@ -1446,14 +1442,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन उंगलियों से ऊपर की ओर स्वाइप करें और दबाकर रखें. जेस्चर की ज़्यादा जानकारी पाने के लिए टैप करें."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सभी ऐप्लिकेशन देखने के लिए, कीबोर्ड का इस्तेमाल करें"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"किसी भी समय ऐक्शन बटन दबाएं. हाथ के जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा, अब ब्राइटनेस स्लाइडर का हिस्सा है"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"अब ब्राइटनेस लेवल घटाकर, स्क्रीन की रोशनी को सामान्य लेवल से और कम किया जा सकता है.\n\nयह सुविधा ब्राइटनेस स्लाइडर का हिस्सा है. इसलिए, स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा वाले शॉर्टकट हटा दिए गए हैं."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा वाले शॉर्टकट हटाएं"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा वाले शॉर्टकट हटा दिए गए हैं"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"कनेक्टिविटी"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"सुलभता"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"काम की सेवाएं"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index c34c07e..4055e74 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvučnici i zasloni"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predloženi uređaji"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Ulaz"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Izlaz"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Zaustavite dijeljenu sesiju da biste premjestili medij na drugi uređaj"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Zaustavi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako emitiranje funkcionira"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Dodaj"</string>
     <string name="manage_users" msgid="1823875311934643849">"Upravljanje korisnicima"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Ova obavijest ne podržava povlačenje na podijeljeni zaslon."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokacija je aktivna"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi nije dostupan"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioritetni način rada"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je postavljen"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Prilagodite zaključavanje zaslona"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Otključajte da biste prilagodili zaključani zaslon"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi nije dostupan"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokacija je aktivna"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera je blokirana"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Blokirani su kamera i mikrofon"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon je blokiran"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tipkovnice"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tipkovnim prečacima"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima za dodirnu podlogu, tipkovnim prečacima i ostalom"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za povratak"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za otvaranje početnog zaslona"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Pregled nedavnih aplikacija"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Da biste se vratili natrag, s tri prsta prijeđite ulijevo ili udesno bilo gdje na dodirnoj podlozi.\n\nZa to možete upotrijebiti i tipku za radnju tipkovnog prečaca + ESC."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Da biste u bilo kojem trenutku otvorili početni zaslon, trima prstima prijeđite prema gore od dna zaslona."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Odlično!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Izvršili ste pokret za otvaranje početnog zaslona."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Pregled nedavnih aplikacija"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Prijeđite prema gore trima prstima na dodirnoj podlozi i zadržite pritisak."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavno korištenih aplikacija."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tipka za radnju"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Da biste pristupili svojim aplikacijama, pritisnite tipku za radnje na tipkovnici."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prijeđite prema gore trima prstima i zadržite pritisak. Dodirnite da biste naučili više pokreta."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Upotrijebite tipkovnicu za prikaz svih aplikacija"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku za radnju u bilo kojem trenutku. Dodirnite da biste naučili više pokreta."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Dodatno zatamnjenje sada je dio klizača za svjetlinu"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Zaslon možete dodatno zatamniti daljnjim smanjivanjem razine svjetline.\n\nBudući da je ta značajka sada dio klizača za svjetlinu, prečaci za dodatno zatamnjenje uklanjaju se."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Ukloni prečace za dodatno zatamnjenje"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Uklonjeni su prečaci za dodatno zatamnjenje"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Povezivost"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Pristupačnost"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Uslužni programi"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index abc7bd0..685e219 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hangfalak és kijelzők"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Javasolt eszközök"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Állítsa le a megosztott munkamenetet, ha át szeretné helyezni a médiát egy másik eszközre"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Leállítás"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"A közvetítés működése"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Hozzáadás"</string>
     <string name="manage_users" msgid="1823875311934643849">"Kezelés"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Az értesítés nem támogatja az osztott képernyőre való áthúzást."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"A helyinformáció aktív"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"A Wi‑Fi nem áll rendelkezésre"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioritás mód"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Ébresztő beállítva"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Lezárási képernyő testreszabása"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Oldja fel a zárolást a lezárási képernyő testreszabásához"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Nem áll rendelkezésre Wi-Fi"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"A helyinformáció aktív"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera letiltva"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera és mikrofon letiltva"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon letiltva"</string>
@@ -1396,6 +1398,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
+    <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigáció a billentyűzet segítségével"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Billentyűparancsok megismerése"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigálás az érintőpaddal"</string>
@@ -1404,8 +1407,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Érintőpad-kézmozdulatok, billentyűparancsok és egyebek megismerése"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Vissza kézmozdulat"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Kezdőképernyő kézmozdulat"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Legutóbbi alkalmazások megtekintése"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kész"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Vissza"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"A visszalépéshez csúsztasson három ujjal balra vagy a jobbra az érintőpadon.\n\nEnnek végrehajtásához használhatja az Action + Esc billentyűparancsot is."</string>
@@ -1415,14 +1417,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Ha bármikor vissza szeretne térni a kezdőképernyőre, csúsztassa gyorsan felfelé három ujját a képernyő aljáról."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Remek!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Teljesítette a kezdőképernyőre lépés kézmozdulatát."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Legutóbbi alkalmazások megtekintése"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Csúsztasson felfelé három ujjal az érintőpadon, és tartsa lenyomva az ujjait."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kiváló!"</string>
+    <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="tutorial_action_key_title" msgid="2659466586996495447">"Műveletbillentyű"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Az alkalmazásokhoz való hozzáféréshez nyomja meg a billentyűzet műveletbillentyűjét."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulálunk!"</string>
@@ -1446,14 +1444,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Gyúsztason felfelé három ujjal, és tartsa lenyomva az ujjait. Koppintson a további kézmozdulatokért."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"A billentyűzet használatával valamennyi alkalmazás megtekinthető"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"A műveletbillentyű bármikor használható. Koppintson a további kézmozdulatokért."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Az extrasötét funkció mostantól része a fényerő-beállítási csúszkának"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Mostantól extrasötétre állíthatja a képernyőt, amivel a korábbinál még jobban csökkentheti a fényerőt.\n\nMivel az extrasötét funkció mostantól a fényerő-szabályozó csúszka része, eltávolítjuk a parancsikonjait."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Az extrasötét funkció parancsikonjainak eltávolítása"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Az extrasötét funkció parancsikonjai eltávolítva"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Kapcsolódás"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Kisegítő lehetőségek"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Segédprogramok"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index d25cb52..36d3e42 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Ծանուցումներ չկան"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Նոր ծանուցումներ չկան"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Ծանուցումների ձայնի իջեցումը միացված է"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Սարքի և ծանուցումների ձայնն ավտոմատ իջեցվում է մինչև 2 րոպե, երբ շատ ծանուցումներ եք ստանում։"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Սարքի և ծանուցումների ձայնն ավտոմատ իջեցվում է մինչև 2 րոպեով, երբ շատ ծանուցումներ եք ստանում։"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Անջատել"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ապակողպեք՝ տեսնելու հին ծանուցումները"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Այս սարքը կառավարում է ձեր ծնողը"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Բարձրախոսներ և էկրաններ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Առաջարկվող սարքեր"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Կանգնեցրեք ընդհանուր աշխատաշրջանը՝ մուլտիմեդիա բովանդակությունն այլ սարք տեղափոխելու համար"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Կանգնեցնել"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ինչպես է աշխատում հեռարձակումը"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Ավելացնել"</string>
     <string name="manage_users" msgid="1823875311934643849">"Կառավարել"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Այս ծանուցումը հնարավոր չէ քաշել տրոհված էկրանի մեկ հատվածից մյուսը"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"«Տեղադրություն» թույլտվությունն ակտիվ է"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi-ը հասանելի չէ"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Առաջնահերթության ռեժիմ"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Զարթուցիչը դրված է"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Անհատականացնել կողպէկրանը"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Ապակողպեք սարքը՝ կողպէկրանը կարգավորելու համար"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi ցանց հասանելի չէ"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"«Տեղադրություն» թույլտվությունն ակտիվ է"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Տեսախցիկն արգելափակված է"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Տեսախցիկն ու խոսափողը արգելափակված են"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Խոսափողն արգելափակված է"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Կողմնորոշվեք ձեր ստեղնաշարի օգնությամբ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Սովորեք օգտագործել ստեղնային դյուրանցումները"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Կողմնորոշվեք ձեր հպահարթակի օգնությամբ"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Սովորեք օգտագործել հպահարթակի ժեստերը, ստեղնային դյուրանցումները և ավելին"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"«Հետ» ժեստ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Հիմնական էկրան անցնելու ժեստ"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Դիտել վերջին հավելվածները"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Պատրաստ է"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Հետ գնալ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Հետ գնալու համար հպահարթակի վրա երեք մատով սահեցրեք ձախ կամ աջ։\n\nԻնչպես նաև կարող եք օգտագործել ստեղնային դյուրանցման գործողությունը + Esc։"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Հիմնական էկրան վերադառնալու համար երեք մատը էկրանի ներքևից սահեցրեք վերև։"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Գերազանց է"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը։"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Դիտել վերջին հավելվածները"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Երեք մատը սահեցրեք վերև և սեղմած պահեք հպահարթակին։"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Կեցցե՛ք։"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Դուք կատարեցիք վերջին օգտագործված հավելվածների դիտման ժեստը։"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Գործողության ստեղն"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Բոլոր հավելվածներն օգտագործելու համար սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Շնորհավո՛ր"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Երեք մատը սահեցրեք վերև և սեղմած պահեք։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Օգտագործեք ձեր ստեղնաշարը՝ բոլոր հավելվածները դիտելու համար"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Ցանկացած ժամանակ սեղմեք գործողության ստեղնը։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Հավելյալ խամրեցումն այժմ պայծառության սահիչի մասն է"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Այժմ դուք կարող եք հավելյալ խամրեցնել էկրանը՝ էլ ավելի նվազեցնելով պայծառության մակարդակը։\n\nՔանի որ այս գործառույթն այժմ պայծառության սահիչի մասն է, հավելյալ խամրեցման դրյուրանցումները հեռացվում են։"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Հեռացնել հավելյալ խամրեցման դյուրանցումները"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Հավելյալ խամրեցման դյուրանցումները հեռացվել են"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Կապ"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Հատուկ գործառույթներ"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Կոմունալ ծառայություններ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index bf1085d..a531725 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Tidak ada notifikasi"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Tidak ada notifikasi baru"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Pengurangan suara dan getaran notifikasi aktif"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Saat Anda menerima terlalu banyak notifikasi sekaligus, volume dan notifikasi perangkat akan otomatis dikurangi hingga selama 2 menit."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Saat Anda menerima terlalu banyak notifikasi sekaligus, volume dan getaran perangkat akan otomatis dikurangi hingga selama 2 menit."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Nonaktifkan"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat notifikasi lama"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Perangkat ini dikelola oleh orang tuamu"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker &amp; Layar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Perangkat yang Disarankan"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Menghentikan sesi berbagi Anda untuk memindahkan media ke perangkat lain"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Berhenti"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara kerja siaran"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Tambahkan"</string>
     <string name="manage_users" msgid="1823875311934643849">"Kelola pengguna"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Notifikasi ini tidak mendukung fitur tarik ke layar terpisah"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokasi aktif"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi tidak tersedia"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Mode prioritas"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm disetel"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Sesuaikan layar kunci"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Buka kunci untuk menyesuaikan layar kunci"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi tidak tersedia"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokasi aktif"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera diblokir"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera dan mikrofon diblokir"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon diblokir"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Menavigasi menggunakan keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Pelajari pintasan keyboard"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Menavigasi menggunakan touchpad"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Pelajari gestur touchpad, pintasan keyboard, dan lainnya"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gestur kembali"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gestur layar utama"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat aplikasi terbaru"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Untuk kembali, geser ke kiri atau kanan menggunakan tiga jari di touchpad.\n\nAnda juga dapat menggunakan pintasan keyboard Action + ECS untuk melakukannya."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Untuk membuka layar utama kapan saja, geser ke atas menggunakan tiga jari dari bawah layar Anda."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bagus!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Anda telah menyelesaikan gestur buka layar utama."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Lihat aplikasi terbaru"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Geser ke atas dan tahan menggunakan tiga jari di touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bagus!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda telah menyelesaikan gestur untuk melihat aplikasi terbaru."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tombol tindakan"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Untuk mengakses aplikasi, tekan tombol tindakan di keyboard."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Selamat!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Geser ke atas dan tahan menggunakan tiga jari. Ketuk untuk mempelajari gestur lainnya."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gunakan keyboard untuk melihat semua aplikasi"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tekan tombol tindakan kapan saja. Ketuk untuk mempelajari gestur lainnya."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Ekstra redup kini menjadi bagian dari penggeser kecerahan"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Anda kini dapat membuat layar menjadi ekstra redup dengan menurunkan tingkat kecerahan lebih banyak lagi.\n\nKarena fitur ini kini menjadi bagian dari penggeser kecerahan, pintasan ekstra redup akan dihapus."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Hapus pintasan ekstra redup"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Pintasan ekstra redup dihapus"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Konektivitas"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Aksesibilitas"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitas"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 987a51d..c2ada41 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hátalarar og skjáir"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Tillögur að tækjum"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stöðvaðu sameiginlega lotu til að flytja efni yfir í annað tæki"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stöðva"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Svona virkar útsending"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Bæta við"</string>
     <string name="manage_users" msgid="1823875311934643849">"Stjórna notendum"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Þessi tilkynning styður ekki að draga yfir á skiptan skjá."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Staðsetning virk"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"WiFi ekki tiltækt"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Forgangsstilling"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Vekjari stilltur"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Sérsníða lásskjá"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Taktu úr lás til að sérsníða lásskjá"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi er ekki til staðar"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Staðsetning virk"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Lokað fyrir myndavél"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Lokað fyrir myndavél og hljóðnema"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Lokað fyrir hljóðnema"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Flettu með því að nota lyklaborðið"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Kynntu þér flýtilykla"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Flettu með því að nota snertiflötinn"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Kynntu þér bendingar á snertifleti, flýtilykla og fleira"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Bending til að fara til baka"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Bending til að fara á upphafsskjá"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Sjá nýleg forrit"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Lokið"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Til baka"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Strjúktu til vinstri eða hægri með þremur fingrum hvar sem er á snertifletinum til að fara til baka.\n\nÞú getur einnig notað flýtileiðaraðgerðina + ESC til að gera þetta."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Strjúktu upp frá neðri brún skjásins með þremur fingrum til að opna heimaskjáinn."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Flott!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Þú laukst við að kynna þér bendinguna „heim“."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Sjá nýleg forrit"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Strjúktu upp og haltu þremur fingrum inni á snertifletinum."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Vel gert!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Þú framkvæmdir bendinguna til að sjá nýleg forrit."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Aðgerðalykill"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ýttu á aðgerðalykilinn á lyklaborðinu til að opna forritin þín."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Til hamingju!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Strjúktu upp og haltu með þremur fingrum. Ýttu til að læra fleiri bendingar."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Notaðu lyklaborðið til að sjá öll forrit"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Ýttu hvenær sem er á aðgerðalykilinn. Ýttu til að læra fleiri bendingar."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Nú er stillingin „Mjög dökkt“ hluti af birtusleðanum"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Nú geturðu gert skjáinn mjög dökkan með því að lækka birtustigið enn frekar.\n\nÞar sem þessi eiginleiki er nú hluti af birtusleðanum verða flýtilyklar fyrir mjög dökka stillingu fjarlægðir."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Fjarlægja flýtilykla fyrir mjög dökka stillingu"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Flýtilyklar fyrir mjög dökka stillingu verða fjarlægðir"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Tengigeta"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Aðgengileiki"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Aukabúnaður"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index e2733b1..4ff2eda 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Nessuna notifica"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Nessuna nuova notifica"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Attenuazione delle notifiche attivata"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e avvisi sono ridotti automaticamente fino a 2 minuti quando ricevi troppe notifiche insieme."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Volume e avvisi vengono ridotti automaticamente per un massimo di 2 minuti quando ricevi troppe notifiche contemporaneamente."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Disattiva"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Sblocca per vedere le notifiche meno recenti"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Questo dispositivo è gestito dai tuoi genitori"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speaker e display"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivi consigliati"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Interrompi la sessione condivisa per spostare i contenuti multimediali su un altro dispositivo"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Interrompi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Come funziona la trasmissione"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Aggiungi"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gestisci utenti"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Non è possibile trascinare questa notifica tra le due parti dello schermo diviso"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Posizione attiva"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi non disponibile"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modalità Priorità"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Sveglia impostata"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizza schermata di blocco"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Sblocca per personalizzare la schermata di blocco"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi non disponibile"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Posizione attiva"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Videocamera bloccata"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Videocamera e microfono bloccati"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microfono bloccato"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviga usando la tastiera"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informazioni sulle scorciatoie da tastiera"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviga usando il touchpad"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Scopri gesti con il touchpad, scorciatoie da tastiera e altro ancora"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto Indietro"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto Home"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Visualizza app recenti"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fine"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Indietro"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Per tornare indietro, scorri verso sinistra o verso destra utilizzando tre dita in un punto qualsiasi del touchpad.\n\nPuoi usare anche la scorciatoia da tastiera Action + Esc per farlo."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Per andare alla schermata Home, scorri verso l\'alto con tre dita dalla parte inferiore dello schermo."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bene!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Hai completato il gesto Vai alla schermata Home."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Visualizza app recenti"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Scorri verso l\'alto e tieni premuto con tre dita sul touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ottimo lavoro."</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Hai completato il gesto Visualizza app recenti."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tasto azione"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Per accedere alle tue app, premi il tasto azione sulla tastiera."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Complimenti!"</string>
@@ -1446,26 +1445,15 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Scorri verso l\'alto e tieni premuto con tre dita. Tocca per scoprire altri gesti."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa la tastiera per visualizzare tutte le app"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Premi il tasto azione in qualsiasi momento. Tocca per scoprire altri gesti."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Ora l\'attenuazione extra è nel cursore della luminosità"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Ora puoi usare l\'attenuazione extra per lo schermo abbassando il livello di luminosità ancora di più.\n\nDato che questa funzionalità ora fa parte del cursore della luminosità, le scorciatoie per l\'attenuazione extra vengono rimosse."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Rimuovi scorciatoie attenuazione extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Scorciatoie attenuazione extra rimosse"</string>
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connettività"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibilità"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilità"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Privacy"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Forniti dalle app"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Sconosciuti"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 2a006a8..1c212e1 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -440,7 +440,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"מצב מופעל"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"פועל • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"מצב מושבת"</string>
-    <string name="zen_mode_set_up" msgid="7457957033034460064">"הגדרה"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"להגדרה"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"שינוי ב\'הגדרות\'"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{אין מצבים פעילים}=1{מצב פעיל אחד ({mode})}one{‫# מצבים פעילים}two{‫# מצבים פעילים}other{‫# מצבים פעילים}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות, תזכורות, אירועים ושיחות ממתקשרים מסוימים לבחירתך. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string>
@@ -576,7 +576,7 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"אין התראות"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"אין התראות חדשות"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"הפוגת ההתראות מופעלת"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"הפוגת התראות מופעלת"</string>
     <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"עוצמת הקול וההתראות במכשיר מופחתות אוטומטית למשך עד 2 דקות כשמתקבלות יותר מדי התראות בבת אחת."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"השבתה"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"יש לבטל את הנעילה כדי לראות התראות ישנות"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"‎<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%‎‎"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"רמקולים ומסכים"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"הצעות למכשירים"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"עצירת הסשן המשותף כדי להעביר מדיה למכשיר אחר"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"עצירה"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"הסבר על שידורים"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"הוספה"</string>
     <string name="manage_users" msgid="1823875311934643849">"ניהול משתמשים"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ההתראה הזו לא תומכת בגרירה למסך מפוצל"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"המיקום פעיל"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"‏Wi‑Fi לא זמין"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"מצב עדיפות"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ההתראה מוגדרת"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"התאמה אישית של מסך הנעילה"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"כדי להתאים אישית את מסך הנעילה, יש לבטל את הנעילה"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"‏ה-Wi-Fi לא זמין"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"המיקום פעיל"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"המצלמה חסומה"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"המצלמה והמיקרופון חסומים"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"המיקרופון חסום"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ניווט באמצעות המקלדת"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"מידע על מקשי קיצור"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ניווט באמצעות לוח המגע"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"מידע על התנועות בלוח המגע, מקשי קיצור ועוד"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"תנועת חזרה"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"תנועת חזרה למסך הבית"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"הצגת האפליקציות האחרונות"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"סיום"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"חזרה"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"‏כדי לחזור אחורה, מחליקים שמאלה או ימינה עם שלוש אצבעות בכל מקום על לוח המגע.\n\nאפשר לבצע את הפעולה הזו גם באמצעות קיצור הדרך לפעולה + מקש ESC."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"כדי לעבור למסך הבית בכל שלב, צריך להחליק למעלה עם שלוש אצבעות מהחלק התחתון של המסך."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"איזה יופי!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"השלמת את תנועת המעבר למסך הבית."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"הצגת האפליקציות האחרונות"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות על לוח המגע."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"מעולה!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"השלמת את התנועה להצגת האפליקציות האחרונות."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"מקש הפעולה"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"כדי לגשת לאפליקציות, מקישים על מקש הפעולה במקלדת."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"כל הכבוד!"</string>
@@ -1446,26 +1445,15 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"איך להשתמש במקלדת כדי לראות את כל האפליקציות"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"בכל שלב אפשר ללחוץ על מקש הפעולה. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"התכונה \'מעומעם במיוחד\' נוספה לפס ההזזה לבהירות"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"עכשיו אפשר להפוך את המסך למעומעם במיוחד באמצעות הפחתה נוספת של רמת הבהירות.\n\nהתכונה הזו היא עכשיו חלק מפס ההזזה לבהירות, לכן קיצורי הדרך לתכונה \'מעומעם במיוחד\' נמצאים בתהליך הסרה."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"הסרה של קיצורי הדרך לתכונה \'מעומעם במיוחד\'"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"קיצורי הדרך לתכונה \'מעומעם במיוחד\' הוסרו"</string>
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"קישוריות"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"נגישות"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"כלי תחזוקה"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"פרטיות"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"מסופקים על ידי אפליקציות"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"מסך"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"לא ידוע"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 780254f..eda727f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"通知はありません"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"新しい通知はありません"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"通知のクールダウンが ON になっています"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"一度に多くの通知が届いたときに、最大 2 分間自動的にデバイスの音量が小さくなりアラートも減ります。"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"一度に多くの通知が届いた場合に、最長 2 分間自動的にデバイスの音量が小さくなりアラートも減ります。"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"OFF にする"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ロック解除して以前の通知を表示"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"このデバイスは保護者によって管理されています"</string>
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"スピーカーとディスプレイ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"デバイスの候補"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"入力"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"出力"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"メディアを他のデバイスに移動する共有中のセッションを停止します。"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"停止"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ブロードキャストの仕組み"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"追加"</string>
     <string name="manage_users" msgid="1823875311934643849">"ユーザーの管理"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"この通知は、分割画面へのドラッグをサポートしていません"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"位置情報がアクティブです"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi-Fi を利用できません"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"優先順位モード"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"アラームを設定しました"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ロック画面のカスタマイズ"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ロック画面をカスタマイズするにはロックを解除してください"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi は利用できません"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"位置情報がアクティブです"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"カメラはブロックされています"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"カメラとマイクはブロックされています"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"マイクはブロックされています"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"キーボードを使用して移動する"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"キーボード ショートカットの詳細"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"タッチパッドを使用して移動する"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"タッチパッド操作やキーボード ショートカットなどの詳細"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"「戻る」ジェスチャー"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"「ホーム」ジェスチャー"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"最近使ったアプリを表示する"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完了"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"戻る"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"戻るには、3 本の指でタッチパッドを左右にスワイプします。\n\nキーボード ショートカットのアクション + ESC キーを使用して、この操作を行うこともできます。"</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"3 本の指で画面を下から上にスワイプすると、ホーム画面にいつでも移動できます。"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"お疲れさまでした。"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"「ホームに移動」操作を学習しました。"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"最近使ったアプリを表示する"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"タッチパッドを 3 本の指で上にスワイプして長押しします。"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"よくできました"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"「最近使ったアプリを表示する」操作を学習しました。"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"アクションキー"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"アプリにアクセスするには、キーボードのアクションキーを押します。"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"お疲れさまでした。"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"3 本の指で上にスワイプして長押しします。ジェスチャーの詳細を確認するにはタップしてください。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"キーボードを使用して、すべてのアプリを表示する"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"アクションキーを押せばいつでも機能します。ジェスチャーの詳細を確認するにはタップしてください。"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"「さらに輝度を下げる」が明るさ調整スライダーに追加されました"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"明るさを大幅に低く設定することで、画面の輝度をさらに下げられるようになりました。\n\nこの機能が明るさ調整スライダーに追加されたため、「さらに輝度を下げる」のショートカットは削除されます。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"「さらに輝度を下げる」のショートカットを削除"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"「さらに輝度を下げる」のショートカットを削除しました"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"接続"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ユーザー補助"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ユーティリティ"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index dab2619..836bd44 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და დისპლეები"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"შემოთავაზებული მოწყობილობები"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"შემავალი"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"გამომავალი"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"შეწყვიტეთ გაზიარებული სესია, რათა მულტიმედია სხვა მოწყობილობაზე გადაიტანოთ"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"შეწყვეტა"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ტრანსლირების მუშაობის პრინციპი"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"დამატება"</string>
     <string name="manage_users" msgid="1823875311934643849">"მართვა"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ამ შეტყობინების გადათრევა გაყოფილ ეკრანებს შორის არ არის მხარდაჭერილი."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"მდებარეობა აქტიურია"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi მიუწვდომელია"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"პრიორიტეტული რეჟიმი"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"მაღვიძარა დაყენებულია"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ჩაკეთილი ეკრანის მორგება"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ჩაკეტილი ეკრანის მოსარგებად გაბლოკეთ"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi მიუწვდომელია"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"მდებარეობა აქტიურია"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"კამერა დაბლოკილია"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"კამერა და მიკროფონი დაბლოკილია"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"მიკროფონი დაბლოკილია"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ნავიგაცია კლავიატურის გამოყენებით"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"კლავიატურის მალსახმობების სწავლა"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ნავიგაცია სენსორული პანელის გამოყენებით"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"სენსორული პანელის ჟესტების, კლავიატურის მალსახმობების და სხვა ფუნქციების სწავლა"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"უკან დაბრუნების ჟესტი"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"მთავარ ეკრანზე გადასვლის ჟესტი"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ბოლო აპების ნახვა"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"მზადაა"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"უკან დაბრუნება"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"უკან დასაბრუნებლად სენსორულ პანელზე გადაფურცლეთ მარცხნივ ან მარჯვნივ სამი თითის გამოყენებით ნებისმიერ ადგილას.\n\nამისთვის თქვენ ასევე შეგიძლიათ გამოიყენოთ კლავიატურის მალსახმობის მოქმედება + ESC."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"თქვენს მთავარ ეკრანზე ნებისმიერ დროს გადასასვლელად გადაფურცლეთ ეკრანის ქვემოდან ზემოთ სამი თითით."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"მშვენიერია!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"თქვენ შეასრულეთ მთავარ ეკრანზე დაბრუნების ჟესტი."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ბოლო აპების ნახვა"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"თქვენს სენსორულ პანელზე სამი თითით გადაფურცლეთ ზემოთ და მოიცადეთ."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"შესანიშნავია!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"თქვენ დაასრულეთ ბოლო აპების ხედის ჟესტი."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"მოქმედების კლავიში"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"აპებზე წვდომისთვის დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"გილოცავთ!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"სამი თითით გადაფურცლეთ ზემოთ და მოიცადეთ. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ყველა აპის სანახავად გამოიყენეთ თქვენი კლავიატურა"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ნებისმიერ დროს დააჭირეთ მოქმედების კლავიშს. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"დამატებითი დაბინდვის ფუნქცია ახლა სიკაშკაშის სლაიდერზეა განთავსებული"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ეკრანის დამატებითი დაბინდვა უკვე სიკაშკაშის დონის კიდევ უფრო შემცირების გზით შეგიძლიათ.\n\nვინაიდან ეს ფუნქცია უკვე სიკაშკაშის სლაიდერის ნაწილია, ამოშლილია დამატებითი დაბინდვის მალსახმობები."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"დამატებითი დაბინდვის მალსახმობების ამოშლა"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"დამატებითი დაბინდვის მალსახმობები ამოშლილია"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"კავშირი"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"მარტივი წვდომა"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ხელსაწყოები"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 53ef57e..dc2a618 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер мен дисплейлер"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ұсынылған құрылғылар"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Мультимедиа файлын басқа құрылғыға жылжыту үшін ортақ сеансты тоқтатыңыз."</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Тоқтату"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Тарату қалай жүзеге асады"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Қосу"</string>
     <string name="manage_users" msgid="1823875311934643849">"Параметрлер"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Бұл хабарландыруды бөлінген экранға сүйреп апару мүмкін емес."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Локация қосулы"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi қолжетімсіз"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Басымдық режимі"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Оятқыш орнатылды"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Құлып экранын бейімдеу"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Құлып экранын бейімдеу үшін құлыпты ашыңыз"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi қолжетімсіз."</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Локация қосулы"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камера блокталған."</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камера мен микрофон блокталған."</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофон блокталған."</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Пернетақтамен жұмыс істеңіз"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Перне тіркесімдерін үйреніңіз."</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Сенсорлық тақтамен жұмыс істеңіз"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсорлық тақта қимылдарын, перне тіркесімдерін және т.б. үйреніңіз."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Артқа қайтару қимылы"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Негізгі бетке қайтару қимылы"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Соңғы қолданбаларды көру"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Дайын"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артқа"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Артқа қайту үшін сенсорлық тақтаның кез келген жерін үш саусақпен солға не оңға сырғытыңыз.\n\nСондай-ақ Action + ESC перне тіркесімін пайдалануға болады."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Негізгі экранға кез келген уақытта өту үшін экранның төменгі жағынан жоғары қарай үш саусағыңызбен сырғытыңыз."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Жақсы нәтиже!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Негізгі экранға қайту қимылын аяқтадыңыз."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Соңғы қолданбаларды көру"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Сенсорлық тақтада үш саусақпен жоғары сырғытып, басып тұрыңыз."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Жарайсыз!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Соңғы қолданбаларды көру қимылын орындадыңыз."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Әрекет пернесі"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Қолданбаларыңызға кіру үшін пернетақтадағы әрекет пернесін басыңыз."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Құттықтаймыз!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Үш саусақпен жоғары сырғытып, басып тұрыңыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Барлық қолданбаны көру үшін пернетақтаны қолданыңыз"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Әрекет пернесін кез келген уақытта баса аласыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Экранды қарайту функциясы енді жарықтық жүгірткісіне қосылды"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Экранның жарықтық деңгейін көбірек төмендету арқылы оны қарайта аласыз.\n\nБұл функция енді жарықтық жүгірткісіне қосылғандықтан, экранды қарайту жылдам пәрмендері өшіріліп жатыр."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Экранды қарайту жылдам пәрмендерін өшіру"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Экранды қарайту жылдам пәрмендері өшірілді."</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Қосылу мүмкіндігі"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Арнайы мүмкіндіктер"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Утилиталар"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e2bba53..6c15c79 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ឧបករណ៍បំពងសំឡេង និងផ្ទាំងអេក្រង់"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ឧបករណ៍​ដែលបានណែនាំ"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ឧបករណ៍​បញ្ចូល"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"ឧបករណ៍បញ្ចេញ"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"បញ្ឈប់វគ្គដែលអ្នក​បានចែករំលែក ដើម្បីផ្លាស់ទីមេឌៀ​ទៅឧបករណ៍​ផ្សេងទៀត"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"បញ្ឈប់"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"របៀបដែលការផ្សាយដំណើរការ"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"បញ្ចូល"</string>
     <string name="manage_users" msgid="1823875311934643849">"គ្រប់គ្រង​អ្នក​ប្រើប្រាស់"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ការជូនដំណឹងនេះមិនអាចឱ្យអូសដើម្បីបំបែកអេក្រង់បានទេ"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ទីតាំងដែលសកម្ម"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi ត្រូវបានបិទ"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"មុខងារ​អាទិភាព"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"រូបកំណត់​ម៉ោងរោទ៍"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ប្ដូរអេក្រង់ចាក់សោ​តាមបំណង"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ដោះសោ ដើម្បីប្ដូរអេក្រង់ចាក់សោតាមបំណង"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"មិនមាន Wi-Fi ទេ"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ទីតាំងដែលសកម្ម"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"បាន​ទប់ស្កាត់​កាមេរ៉ា"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"បានទប់ស្កាត់​កាមេរ៉ា និង​មីក្រូហ្វូន"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"បាន​ទប់ស្កាត់​មីក្រូហ្វូន"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"រុករកដោយប្រើក្ដារចុចរបស់អ្នក"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ស្វែងយល់អំពីផ្លូវកាត់​ក្ដារ​ចុច"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"រុករកដោយប្រើផ្ទាំងប៉ះរបស់អ្នក"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ ផ្លូវកាត់​ក្ដារ​ចុច និងអ្វីៗជាច្រើនទៀត"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ចលនាថយក្រោយ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ចលនាទៅទំព័រដើម"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"មើលកម្មវិធីថ្មីៗ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"រួចរាល់"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ថយ​ក្រោយ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ដើម្បីថយក្រោយ សូមអូសទៅឆ្វេង ឬស្ដាំដោយប្រើ​​ម្រាមដៃបីនៅត្រង់ណាក៏បានលើផ្ទាំងប៉ះ។\n\nអ្នកក៏អាចប្រើសកម្មភាពផ្លូវកាត់ក្ដារចុច + ESC សម្រាប់ការធ្វើបែបនេះ។"</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ដើម្បីចូលទៅអេក្រង់ដើមរបស់អ្នកនៅពេលណាក៏បាន សូមអូសឡើងលើដោយប្រើម្រាមដៃបីពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក។"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ល្អ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"អ្នក​បានបញ្ចប់​ចលនា​ចូលទៅកាន់​ទំព័រដើម​ហើយ។"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"មើលកម្មវិធីថ្មីៗ"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"អូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបីលើផ្ទាំងប៉ះរបស់អ្នក។"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ធ្វើបានល្អ!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"អ្នកបានបញ្ចប់ការមើលចលនាកម្មវិធីថ្មីៗ។"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"គ្រាប់ចុចសកម្មភាព"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ដើម្បីចូលប្រើប្រាស់កម្មវិធីរបស់អ្នក សូមចុចគ្រាប់ចុចសកម្មភាពនៅលើក្ដារចុចរបស់អ្នក។"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"សូមអបអរសាទរ!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"អូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបី។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ប្រើក្ដារចុចរបស់អ្នក ដើម្បីមើលកម្មវិធីទាំងអស់"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ចុចគ្រាប់ចុចសកម្មភាពនៅពេលណាក៏បាន។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ឥឡូវនេះ មុខងារងងឹតខ្លាំងក្លាយជាផ្នែកមួយនៃគ្រាប់រំកិលពន្លឺ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ឥឡូវនេះ អ្នកអាចធ្វើឱ្យអេក្រង់ងងឹតខ្លាំងបានដោយបន្ថយកម្រិតពន្លឺបន្ថែមទៀត។\n\nដោយសារឥឡូវមុខងារនេះក្លាយជាផ្នែកមួយនៃគ្រាប់រំកិលពន្លឺ ផ្លូវ​កាត់មុខងារងងឹតខ្លាំងកំពុងត្រូវបានដកចេញ។"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"ដកផ្លូវ​កាត់មុខងារងងឹតខ្លាំងចេញ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"ផ្លូវ​កាត់មុខងារងងឹតខ្លាំងត្រូវបានដកចេញ"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"ការតភ្ជាប់"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ភាពងាយស្រួល"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"កម្មវិធី​សម្រួលដំណើរការ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 0b87cbb..a16f110 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ಯಾವುದೇ ಹೊಸ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ನೋಟಿಫಿಕೇಶನ್ ಕೂಲ್‌ಡೌನ್ ಆನ್ ಆಗಿದೆ"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ನೀವು ಏಕಕಾಲದಲ್ಲಿ ತೀರಾ ಹೆಚ್ಚು ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಪಡೆದಾಗ 2 ನಿಮಿಷಗಳವರೆಗೆ ನಿಮ್ಮ ಸಾಧನದ ವಾಲ್ಯೂಮ್ ಮತ್ತು ಅಲರ್ಟ್‌ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತದೆ."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ನೀವು ಏಕಕಾಲದಲ್ಲಿ ತೀರಾ ಹೆಚ್ಚು ನೋಟಿಫಿಕೇಶನ್‌‍‍ಗಳನ್ನು ಪಡೆದಾಗ 2 ನಿಮಿಷಗಳವರೆಗೆ ನಿಮ್ಮ ಸಾಧನದ ವಾಲ್ಯೂಮ್ ಮತ್ತು ಅಲರ್ಟ್‌‍‍ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತದೆ."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ಆಫ್ ಮಾಡಿ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ಹಳೆಯ ಅಧಿಸೂಚನೆಗಳನ್ನು ನೋಡಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ಈ ಸಾಧನವನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ಸ್ಪೀಕರ್‌ಗಳು ಮತ್ತು ಡಿಸ್‌ಪ್ಲೇಗಳು"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ಸೂಚಿಸಿದ ಸಾಧನಗಳು"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"ಮೀಡಿಯಾವನ್ನು ಮತ್ತೊಂದು ಸಾಧನಕ್ಕೆ ಸರಿಸಲು ನಿಮ್ಮ ಹಂಚಿಕೊಂಡ ಸೆಶನ್ ಅನ್ನು ನಿಲ್ಲಿಸಿ"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"ನಿಲ್ಲಿಸಿ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ಪ್ರಸಾರವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"ಸೇರಿಸಿ"</string>
     <string name="manage_users" msgid="1823875311934643849">"ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಹಿಸಿ"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಡ್ರ್ಯಾಗ್ ಮಾಡುವುದನ್ನು ಈ ನೋಟಿಫಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ಸ್ಥಳ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"ವೈ-ಫೈ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ಆದ್ಯತೆ ಮೋಡ್"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ಅಲಾರಾಂ ಹೊಂದಿಸಲಾಗಿದೆ"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ಲಾಕ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"ವೈ-ಫೈ ಲಭ್ಯವಿಲ್ಲ"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ಸ್ಥಳ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ಕ್ಯಾಮರಾವನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೊಫೋನ್‌ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಕಲಿಯಿರಿ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ಟಚ್‌ಪ್ಯಾಡ್ ಗೆಸ್ಚರ್‌ಗಳು, ಕೀಬೋರ್ಡ್‌ಗಳ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ತಿಳಿಯಿರಿ"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ಹಿಂಬದಿ ಗೆಸ್ಚರ್"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ಹೋಮ್ ಗೆಸ್ಚರ್"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ಮುಗಿದಿದೆ"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ಹಿಂತಿರುಗಿ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ಹಿಂತಿರುಗಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಎಲ್ಲಿಯಾದರೂ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಎಡ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ.\n\nಇದಕ್ಕಾಗಿ ನೀವು ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್ Action + ESC ಅನ್ನು ಸಹ ಬಳಸಬಹುದು."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಹೋಗಲು, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಕೆಳಗಿನಿಂದ ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ಭೇಷ್!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ಭೇಷ್‌!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ನೀವು ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳ ಗೆಸ್ಚರ್‌ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ಆ್ಯಕ್ಷನ್‌ ಕೀ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ನಿಮ್ಮ ಆ್ಯಪ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು, ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿರುವ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿರಿ."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ಅಭಿನಂದನೆಗಳು!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಹಾಗೂ ಹೋಲ್ಡ್ ಮಾಡಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಬಳಸಿ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ಯಾವಾಗ ಬೇಕಾದರೂ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿರಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ಇನ್ನಷ್ಟು ಮಬ್ಬು ಈಗ ಬ್ರೈಟ್‌ನೆಸ್‌ ಸ್ಲೈಡರ್‌ನ ಭಾಗವಾಗಿದೆ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ನೀವು ಈಗ ಬ್ರೈಟ್‌ನೆಸ್‌ನ ಮಟ್ಟವನ್ನು ಇನ್ನಷ್ಟು ಕಡಿಮೆ ಮಾಡುವ ಮೂಲಕ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ಇನ್ನಷ್ಟು ಮಬ್ಬುಗೊಳಿಸಬಹುದು.\n\n ಈ ಫೀಚರ್‌ ಈಗ ಬ್ರೈಟ್‌ನೆಸ್ ಸ್ಲೈಡರ್‌ನ ಭಾಗವಾಗಿರುವುದರಿಂದ, ಇನ್ನಷ್ಟು ಮಬ್ಬಾದ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗುತ್ತಿದೆ."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"ಇನ್ನಷ್ಟು ಮಬ್ಬು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"ಇನ್ನಷ್ಟು ಮಬ್ಬು ಶಾರ್ಟ್‌ಕಟ್‌ ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"ಕನೆಕ್ಟಿವಿಟಿ"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ಯುಟಿಲಿಟಿಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 4f4c5b9..622f0fb 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"스피커 및 디스플레이"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"추천 기기"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"미디어를 다른 기기로 이동하려면 공유 세션을 중지하세요."</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"중지"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"브로드캐스팅 작동 원리"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"추가"</string>
     <string name="manage_users" msgid="1823875311934643849">"사용자 관리"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"드래그하여 화면을 분할하는 기능이 지원되지 않는 알림입니다."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"활성 상태인 위치"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi를 이용할 수 없습니다."</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"우선순위 모드입니다."</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"알람이 설정되었습니다."</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"잠금 화면 맞춤 설정"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"잠금 화면 맞춤설정을 위해 잠금 해제"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi를 사용할 수 없음"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"활성 상태인 위치"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"카메라 차단됨"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"카메라 및 마이크 차단됨"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"마이크 차단됨"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"키보드를 사용하여 이동"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키 알아보기"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"터치패드를 사용하여 이동"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"터치패드 동작, 단축키 등 알아보기"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"뒤로 동작"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"홈 동작"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"최근 앱 보기"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"완료"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"뒤로"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"돌아가려면 세 손가락을 사용해 터치패드의 아무 곳이나 왼쪽 또는 오른쪽으로 스와이프합니다.\n\n키보드 단축키 Action + ESC를 사용할 수도 있습니다."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"언제든지 홈 화면으로 이동하려면 세 손가락으로 화면 하단에서 위로 스와이프하세요."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"좋습니다"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"홈으로 이동 동작을 완료했습니다."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"최근 앱 보기"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"터치패드에서 세 손가락을 사용해 위로 스와이프한 후 잠시 기다리세요."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"아주 좋습니다"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"최근 앱 보기 동작을 완료했습니다."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"작업 키"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"앱에 액세스하려면 키보드의 작업 키를 누르세요."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"축하합니다"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"세 손가락을 사용해 위로 스와이프한 다음 잠시 기다리세요. 더 많은 동작을 알아보려면 탭하세요."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"키보드를 사용하여 모든 앱 보기"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"언제든지 작업 키를 누릅니다. 더 많은 동작을 알아보려면 탭하세요."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"이제 \'더 어둡게\' 기능이 밝기 슬라이더에 추가되었습니다"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"이제 밝기 수준을 더 낮춰 화면을 더 어둡게 만들 수 있습니다.\n\n이 기능은 이제 밝기 슬라이더에 포함되므로 \'더 어둡게\' 단축키가 삭제됩니다."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"\'더 어둡게\' 단축키 삭제"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"\'더 어둡게\' 단축키가 삭제되었습니다."</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"연결"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"접근성"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"유틸리티"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 023d69e..0edb3f8 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Билдирме жок"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Жаңы билдирмелер жок"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Билдирмелердин үнүн басаңдатуу күйүк"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Бир убакта өтө көп билдирмелер келгенде, түзмөктүн үнү жана эскертүүлөрдүн саны 2 мүнөткө азайтылат."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Өтө көп билдирме келсе, түзмөктүн үнү 2 мүнөткө басаңдап, эскертүүлөрдүн саны азаят."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Өчүрүү"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Билдирмелерди көрүү үчүн кулпуну ачыңыз"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Бул түзмөктү ата-энең башкарат"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Динамиктер жана дисплейлер"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Сунушталган түзмөктөр"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Медиафайлдарды башка түзмөккө жылдыруу үчүн жалпы сеансыңызды токтотуңуз"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Токтотуу"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Кабарлоо кантип иштейт"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Кошуу"</string>
     <string name="manage_users" msgid="1823875311934643849">"Колдонуучуларды башкаруу"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Бул билдирмени бөлүнгөн экранда сүйрөөгө болбойт."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Жайгашкан жер активдүү"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi жеткиликсиз"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Маанилүү сүйлөшүүлөр режими"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Ойготкуч коюлду"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Кулпу экранын тууралоо"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Кулпуланган экранды тууралоо үчүн кулпусун ачыңыз"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi жеткиликтүү эмес"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Жайгашкан жер активдүү"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камера бөгөттөлдү"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камера менен микрофон бөгөттөлдү"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофон бөгөттөлдү"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Нерселерге баскычтоп аркылуу өтүңүз"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ыкчам баскычтар тууралуу билип алыңыз"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Нерселерге сенсордук такта аркылуу өтүңүз"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсордук тактадагы жаңсоолор, ыкчам баскычтар жана башкалар жөнүндө билип алыңыз"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Артка кайтуу жаңсоосу"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Башкы бетке өтүү жаңсоосу"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Акыркы колдонмолорду көрүү"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Бүттү"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артка кайтуу"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Кайтуу үчүн сенсордук тактанын каалаган жерин үч манжаңыз менен солго же оңго сүрүңүз.\n\nОшондой эле Action + ESC баскычтарынын айкалышын колдоно аласыз."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Каалаган убакта башкы экранга өтүү үчүн экранды үч манжаңыз менен ылдыйдан жогору карай сүрүңүз."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Сонун!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Акыркы колдонмолорду көрүү"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Сенсордук тактаны үч манжаңыз менен өйдө сүрүп, кармап туруңуз."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Азаматсыз!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Акыркы колдонмолорду көрүү жаңсоосун аткардыңыз."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Аракет баскычы"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Бардык колдонмолоруңузду көрүү үчүн баскычтобуңуздагы аракет баскычын басыңыз"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Куттуктайбыз!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Үч манжаңыз менен өйдө сүрүп, кармап туруңуз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Бардык колдонмолорду көрүү үчүн баскычтобуңузду колдонуңуз"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Каалаганда аракет баскычын басыңыз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Кошумча караңгылатуу эми жарыктык сыдырмасында жайгашкан"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Эми экрандын жарыктыктыгынын деңгээлин азайтып, экранды кошумча караңгылата аласыз.\n\nБул функция эми жарыктык сыдырмасынын бир бөлүгү болуп калгандыктан, кошумча караңгылатуунун ыкчам баскычтары өтүрүлөт."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Кошумча караңгылатуу ыкчам баскычтарын өчүрүү"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Кошумча караңгылатуу ыкчам баскычтары өчүрүлдү"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Байланыш"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Атайын мүмкүнчүлүктөр"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Утилиталар"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 67a11d4..242d766 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ລຳໂພງ ແລະ ຈໍສະແດງຜົນ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ອຸປະກອນທີ່ແນະນຳ"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"ຢຸດເຊດຊັນທີ່ແບ່ງປັນຂອງທ່ານເພື່ອຍ້າຍມີເດຍໄປຫາອຸປະກອນອື່ນ"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"ຢຸດ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ການອອກອາກາດເຮັດວຽກແນວໃດ"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"ເພີ່ມ"</string>
     <string name="manage_users" msgid="1823875311934643849">"ຈັດການຜູ້ໃຊ້"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ການແຈ້ງເຕືອນນີ້ບໍ່ຮອງຮັບການລາກເພື່ອແບ່ງໜ້າຈໍ"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ສະຖານທີ່ທີ່ນຳໃຊ້ຢູ່"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"ບໍ່ສາມາດໃຊ້ Wi‑Fi ໄດ້"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ໂໝດຄວາມສຳຄັນ"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ຕັ້ງໂມງປຸກແລ້ວ"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ປັບແຕ່ງໜ້າຈໍລັອກ"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ປົດລັອກເພື່ອປັບແຕ່ງໜ້າຈໍລັອກ"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi ບໍ່ພ້ອມໃຫ້ນຳໃຊ້"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ສະຖານທີ່ທີ່ນຳໃຊ້ຢູ່"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ກ້ອງຖ່າຍຮູບຖືກບລັອກຢູ່"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ກ້ອງຖ່າຍຮູບ ແລະ ໄມໂຄຣໂຟນຖືກບລັອກຢູ່"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ໄມໂຄຣໂຟນຖືກບລັອກຢູ່"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ນຳທາງໂດຍໃຊ້ແປ້ນພິມຂອງທ່ານ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ສຶກສາຄີລັດ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ນຳທາງໂດຍໃຊ້ແຜ່ນສຳຜັດຂອງທ່ານ"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ສຶກສາທ່າທາງຂອງແຜ່ນສຳຜັດ, ຄີລັດ ແລະ ອື່ນໆ"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ທ່າທາງສຳລັບກັບຄືນ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ທ່າທາງສຳລັບໜ້າຫຼັກ"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ເບິ່ງແອັບຫຼ້າສຸດ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ແລ້ວໆ"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ກັບຄືນ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ເພື່ອກັບຄືນ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຊ້າຍ ຫຼື ຂວາບ່ອນໃດກໍໄດ້ເທິງແຜ່ນສຳຜັດ.\n\nທ່ານຍັງສາມາດໃຊ້ຄຳສັ່ງຄີລັດ + ESC ສຳລັບການດຳເນີນການນີ້ໄດ້ນຳ."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ເພື່ອໄປຫາໜ້າຫຼັກຂອງທ່ານຕອນໃດກໍໄດ້, ໃຫ້ປັດຂຶ້ນດ້ວຍສາມນິ້ວຈາກລຸ່ມສຸດຂອງໜ້າຈໍຂອງທ່ານ."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ດີຫຼາຍ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ທ່ານໃຊ້ທ່າທາງໄປໜ້າຫຼັກສຳເລັດແລ້ວ."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ເບິ່ງແອັບຫຼ້າສຸດ"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"ໃຊ້ 3 ນິ້ວປັດຂຶ້ນແລ້ວຄ້າງໄວ້ຢູ່ແຜ່ນສໍາຜັດຂອງທ່ານ."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ດີຫຼາຍ!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ທ່ານເບິ່ງທ່າທາງຂອງແອັບຫຼ້າສຸດສຳເລັດແລ້ວ."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ປຸ່ມຄຳສັ່ງ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ເພື່ອເຂົ້າເຖິງແອັບ, ໃຫ້ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ຂໍສະແດງຄວາມຍິນດີ!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ໃຊ້ 3 ນິ້ວປັດຂຶ້ນ ແລ້ວຄ້າງໄວ້. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ໃຊ້ແປ້ນພິມຂອງທ່ານເພື່ອເບິ່ງແອັບທັງໝົດ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ກົດປຸ່ມຄຳສັ່ງໄດ້ທຸກເວລາ. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ຕອນນີ້ການຫຼຸດແສງເປັນພິເສດເປັນສ່ວນໜຶ່ງຂອງແຖບເລື່ອນຄວາມສະຫວ່າງແລ້ວ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ຕອນນີ້ທ່ານສາມາດເຮັດໃຫ້ໜ້າຈໍມືດລົງເປັນພິເສດໄດ້ໂດຍການຫຼຸດລະດັບຄວາມສະຫວ່າງລົງໃຫ້ຫຼາຍຂຶ້ນ.\n\nເນື່ອງຈາກຕອນນີ້ຄຸນສົມບັດນີ້ເປັນສ່ວນໜຶ່ງຂອງແຖບເລື່ອນຄວາມສະຫວ່າງແລ້ວ, ທາງລັດທີ່ຫຼຸດແສງເປັນພິເສດຈຶ່ງຈະຖືກລຶບອອກ."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"ລຶບທາງລັດທີ່ຫຼຸດແສງເປັນພິເສດອອກ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"ລຶບທາງລັດທີ່ຫຼຸດແສງເປັນພິເສດອອກແລ້ວ"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"ການເຊື່ອມຕໍ່"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ການຊ່ວຍເຂົ້າເຖິງ"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ບໍລິການສາທາລະນູປະໂພກ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c813416..fd62599 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -230,7 +230,7 @@
     <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Atrakinimo pagal veidą nustatymas"</string>
     <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Norint dar kartą nustatyti atrakinimą pagal veidą, dabartinis veido modelis bus ištrintas.\n\nTurite dar kartą nustatyti šią funkciją, kad galėtumėte atrakinti telefoną pagal veidą."</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Nepavyko nustatyti atrakinimo pagal veidą. Eikite į skiltį „Nustatymai“ ir bandykite dar kartą."</string>
-    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Palieskite piršto antspaudo jutiklį"</string>
+    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Palieskite piršto atspaudo jutiklį"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"Tęskite paspaudę atrakinimo piktogramą"</string>
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"Veidas neatpažintas. Naudokite piršto antspaudą."</string>
     <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Garsiakalbiai ir ekranai"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Siūlomi įrenginiai"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Sustabdyti bendrinamą seansą norint perkelti mediją į kitą įrenginį"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Sustabdyti"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kaip veikia transliacija"</string>
@@ -1236,7 +1240,7 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Palieskite, kad sužinotumėte daugiau informacijos"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenustatyta signalų"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"įvesti ekrano užraktą"</string>
-    <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Palieskite piršto antspaudo jutiklį. Tai yra trumpesnis mygtukas telefono šone"</string>
+    <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Palieskite piršto atspaudo jutiklį. Tai yra trumpesnis mygtukas telefono šone"</string>
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Kontrolinio kodo jutiklis"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"nustatytumėte tapatybę"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"pasiektumėte įrenginį"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Pridėti"</string>
     <string name="manage_users" msgid="1823875311934643849">"Tvarkyti naudotojus"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Šio pranešimo vilkimas išskaidyto ekrano režimu nepalaikomas"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Vietovė aktyvi"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"„Wi‑Fi“ ryšys nepasiekiamas"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioriteto režimas"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signalas nustatytas"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Užrakinimo ekrano tinkinimas"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Atrakinę tinkinkite užrakinimo ekraną"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"„Wi-Fi“ ryšys nepasiekiamas"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Vietovė aktyvi"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Fotoaparatas užblokuotas"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Fotoaparatas ir mikrofonas užblokuoti"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofonas užblokuotas"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naršykite naudodamiesi klaviatūra"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Sužinokite apie sparčiuosius klavišus"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naršykite naudodamiesi jutikline dalimi"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sužinokite jutiklinės dalies gestus, sparčiuosius klavišus ir kt."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Grįžimo atgal gestas"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pagrindinio ekrano gestas"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Peržiūrėti naujausias programas"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Atlikta"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Grįžti"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Jei norite grįžti, perbraukite kairėn arba dešinėn trimis pirštais bet kurioje jutiklinės dalies vietoje.\n\nTaip pat galite naudoti šį spartųjį klavišą: veiksmų klavišas + klavišas „Esc“."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Jei norite bet kada pasiekti pagrindinį ekraną, perbraukite aukštyn trim pirštais iš ekrano apačios."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Šaunu!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Atlikote perėjimo į pagrindinį ekraną gestą."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Peržiūrėti naujausias programas"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Jutiklinėje dalyje perbraukite aukštyn trimis pirštais ir palaikykite."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Puiku!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Atlikote naujausių programų peržiūros gestą."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Veiksmų klavišas"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Jei norite pasiekti programas, paspauskite klaviatūros veiksmų klavišą."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Sveikiname!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Perbraukite aukštyn trimis pirštais ir palaikykite. Palieskite, kad sužinotumėte daugiau gestų."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Naudokite klaviatūrą, kad peržiūrėtumėte visas programas"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Bet kuriuo metu paspauskite veiksmų klavišą. Palieskite, kad sužinotumėte daugiau gestų."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Funkcija „Itin blanku“ dabar yra ryškumo šliaužiklio dalis"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Dabar galite padaryti ekraną itin blankų, dar labiau sumažindami ryškumo lygį.\n\nKadangi ši funkcija dabar yra ryškumo šliaužiklio dalis, funkcijos „Itin blanku“ spartieji klavišai bus pašalinti."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Pašalinti funkcijos „Itin blanku“ sparčiuosius klavišus"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Funkcijos „Itin blanku“ spartieji klavišai pašalinti"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Ryšiai"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Pritaikomumas"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Paslaugų programos"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a2e4130..a6b7570 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Skaļruņi un displeji"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Ieteiktās ierīces"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Pārtrauciet savu kopīgoto sesiju, lai pārvietotu multivides saturu uz citu ierīci."</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Pārtraukt"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kā darbojas apraide"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Pievienot"</string>
     <string name="manage_users" msgid="1823875311934643849">"Pārvaldīt"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Šis paziņojums neatbalsta vilkšanu uz sadalīto ekrānu."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Atrašanās vietas noteikšana ir aktīva"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi nav pieejams"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioritātes režīms"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signāls ir iestatīts"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Pielāgot bloķēšanas ekrānu"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Bloķēšanas ekrāna pielāgošana pēc atbloķēšanas"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi nav pieejams"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Atrašanās vietas noteikšana ir aktīva"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera ir bloķēta"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kameras un mikrofona lietošana ir bloķēta"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofons ir bloķēts"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pārvietošanās, izmantojot tastatūru"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Uzziniet par īsinājumtaustiņiem."</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pārvietošanās, izmantojot skārienpaliktni"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Uzziniet par skārienpaliktņa žestiem, īsinājumtaustiņiem un citām iespējām."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Žests pāriešanai atpakaļ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Žests pāriešanai uz sākumu"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Skatīt nesen izmantotās lietotnes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gatavs"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atpakaļ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Lai atgrieztos, ar trīs pirkstiem velciet pa kreisi vai pa labi jebkurā vietā uz skārienpaliktņa.\n\nVarat arī izmantot šim nolūkam īsinājumtaustiņus: darbību taustiņu + Esc."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Lai jebkurā brīdī pārietu uz sākuma ekrānu, ar trim pirkstiem velciet augšup no ekrāna apakšdaļas."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Lieliski!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Nesen izmantoto lietotņu skatīšana"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Skārienpaliktnī ar trīs pirkstiem velciet augšup un turiet."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Lieliski!"</string>
+    <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="tutorial_action_key_title" msgid="2659466586996495447">"Darbību taustiņš"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Lai piekļūtu savām lietotnēm, tastatūrā nospiediet darbību taustiņu."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Apsveicam!"</string>
@@ -1446,26 +1445,15 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Ar trīs pirkstiem velciet augšup un turiet. Lai apgūtu citus žestus, pieskarieties šeit."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Visu lietotņu skatīšana, izmantojot tastatūru"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Jebkurā laikā varat nospiest darbību taustiņu. Lai apgūtu citus žestus, pieskarieties šeit."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Papildu aptumšošana tagad ir iekļauta spilgtuma slīdnī"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Tagad varat veikt ekrāna papildu aptumšošanu, vēl vairāk samazinot spilgtuma līmeni.\n\nTā kā šī funkcija tagad ir iekļauta spilgtuma slīdnī, papildu aptumšošanas saīsnes tiek noņemtas."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Noņemt papildu aptumšošanas saīsnes"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Papildu aptumšošanas saīsnes ir noņemtas."</string>
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Savienojamība"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Pieejamība"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilītprogrammas"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Konfidencialitāte"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Nodrošina lietotnes"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displejs"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nezināma"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index d4b066e..1773e36 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Звучници и екрани"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени уреди"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Влез"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Излез"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Сопрете ја споделената сесија за да ги преместите аудиовизуелните содржини на друг уред"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Сопри"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционира емитувањето"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Додај"</string>
     <string name="manage_users" msgid="1823875311934643849">"Управувајте со корисниците"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Известувањево не поддржува влечење на поделен екран"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Локацијата е активна"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi е недостапна"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Приоритетен режим"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Алармот е наместен"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Приспособете го заклучениот екран"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Отклучување за приспособување на заклучениот екран"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi не е достапно"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Локацијата е активна"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камерата е блокирана"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камерата и микрофонот се блокирани"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофонот е блокиран"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Движете се со користење на тастатурата"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете кратенки од тастатурата"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Движете се со користење на допирната подлога"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете движења за допирната подлога, кратенки од тастатурата и друго"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Движење за назад"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Движење за почетен екран"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прегледајте ги неодамнешните апликации"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"За да се вратите назад, повлечете налево или надесно со три прста каде било на допирната подлога.\n\nЗа ова може да ја користите и кратенката од тастатурата Action + ESC."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"За да одите на вашиот почетен екран кога сакате, повлечете нагоре со три прсти од дното на екранот."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Одлично!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Го научивте движењето за враќање на почетниот екран."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прегледајте ги неодамнешните апликации"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Повлечете нагоре и задржете со три прста на допирната подлога."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Копче за дејство"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"За да пристапите до апликациите, притиснете го копчето за дејство на тастатурата."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Честитки!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Повлечете нагоре и задржете со три прста. Допрете за да научите повеќе движења."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Користете ја тастатурата за да ги видите сите апликации"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Притиснете го копчето за дејство кога сакате. Допрете за да научите повеќе движења."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Отсега „Дополнително затемнување“ е дел од лизгачот за осветленост"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Отсега може да го затемнувате екранот дополнително со намалување на нивото на осветленост уште повеќе.\n\nОтсега функцијава е дел од лизгачот за осветленост, па се отстрануваат кратенките за „Дополнително затемнување“."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Отстрани ги кратенките за „Дополнително затемнување“"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Кратенките за „Дополнително затемнување“ се отстранети"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Поврзливост"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Пристапност"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Услужни програми"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index fbb36b1..05b76d3 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"സ്‌പീക്കറുകളും ഡിസ്പ്ലേകളും"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"നിർദ്ദേശിച്ച ഉപകരണങ്ങൾ"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ഇൻപുട്ട്"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"ഔട്ട്പുട്ട്"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"മീഡിയയെ മറ്റൊരു ഉപകരണത്തിലേക്ക് നീക്കുന്നതിന് നിങ്ങളുടെ പങ്കിട്ട സെഷൻ നിർത്തുക"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"നിർത്തുക"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ബ്രോഡ്‌കാസ്‌റ്റ് എങ്ങനെയാണ് പ്രവർത്തിക്കുന്നത്"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"ചേർക്കുക"</string>
     <string name="manage_users" msgid="1823875311934643849">"ഉപയോക്താക്കളെ മാനേജ് ചെയ്യുക"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"സ്പ്ലിറ്റ് സ്ക്രീനിലേക്ക് വലിച്ചിടുന്നതിനെ ഈ അറിയിപ്പ് പിന്തുണയ്ക്കുന്നില്ല"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ലൊക്കേഷൻ സജീവമാണ്"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"വൈഫൈ ലഭ്യമല്ല"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"മുൻഗണനാ മോഡ്"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"അലാറം സജ്ജീകരിച്ചു"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ലോക്ക് സ്‌ക്രീൻ ഇഷ്ടാനുസൃതമാക്കൂ"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ലോക്ക് സ്ക്രീൻ ഇഷ്ടാനുസൃതമാക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"വൈഫൈ ലഭ്യമല്ല"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ലൊക്കേഷൻ സജീവമാണ്"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ക്യാമറ ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ക്യാമറയും മൈക്രോഫോണും ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"മൈക്രോഫോൺ ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"കീബോർഡ് കുറുക്കുവഴികൾ മനസ്സിലാക്കുക"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ടച്ച്‌പാഡ് ജെസ്ച്ചറുകൾ, കീബോർഡ് കുറുക്കുവഴികൾ എന്നിവയും മറ്റും മനസ്സിലാക്കുക"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"\'മടങ്ങുക\' ജെസ്ച്ചർ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ഹോം ജെസ്‌ച്ചർ"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"പൂർത്തിയായി"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"മടങ്ങുക"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"തിരികെ പോകാൻ, ടച്ച്പാഡിൽ എവിടെയെങ്കിലും മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യുക.\n\nഇതിന് Action + ESC കീബോഡ് കുറുക്കുവഴികളും നിങ്ങൾക്ക് ഉപയോഗിക്കാം."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ഏതുസമയത്തും ഹോം സ്ക്രീനിലേക്ക് പോകാൻ, മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യൂ."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"കൊള്ളാം!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ഹോമിലേക്ക് പോകുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"നിങ്ങളുടെ ടച്ച്പാഡിൽ മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"കൊള്ളാം!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക എന്ന ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action കീ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"നിങ്ങളുടെ ആപ്പുകൾ ആക്‌സസ് ചെയ്യാൻ, നിങ്ങളുടെ കീബോർഡിലെ Action കീ അമർത്തുക."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"അഭിനന്ദനങ്ങൾ!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക. കൂടുതൽ ജെസ്ച്ചറുകളറിയാൻ ടാപ്പ് ചെയ്യൂ."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"എല്ലാ ആപ്പുകളും കാണാൻ നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിക്കുക"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ഏതുസമയത്തും ആക്ഷൻ കീ അമർത്തുക. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"കൂടുതൽ ഡിം ചെയ്യൽ, ഇപ്പോൾ തെളിച്ച സ്ലൈഡറിന്റെ ഭാഗമാണ്"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"തെളിച്ചം വളരെ കുറയ്ക്കുന്നതിലൂടെ നിങ്ങൾക്കിപ്പോൾ സ്ക്രീൻ കൂടുതൽ ഡിം ചെയ്യാനാകും.\n\nഈ ഫീച്ചർ ഇപ്പോൾ തെളിച്ച സ്ലൈഡറിന്റെ ഭാഗമായതിനാൽ, കൂടുതൽ ഡിം ചെയ്യൽ കുറുക്കുവഴികൾ നീക്കം ചെയ്യുകയാണ്."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"കൂടുതൽ ഡിം ചെയ്യൽ കുറുക്കുവഴികൾ നീക്കം ചെയ്യുക"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"കൂടുതൽ ഡിം ചെയ്യൽ കുറുക്കുവഴികൾ നീക്കം ചെയ്തു"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"കണക്റ്റിവിറ്റി"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ഉപയോഗസഹായി"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"യൂട്ടിലിറ്റികൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 57948a27..7a9a38b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Чанга яригч ба дэлгэц"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Санал болгосон төхөөрөмжүүд"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Өөр төхөөрөмж рүү медиа зөөхийн тулд хуваалцсан харилцан үйлдлээ зогсооно уу"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Зогсоох"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Нэвтрүүлэлт хэрхэн ажилладаг вэ?"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Нэмэх"</string>
     <string name="manage_users" msgid="1823875311934643849">"Хэрэглэгчдийг удирдах"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Энэ мэдэгдэл нь дэлгэцийг хуваах горим руу чирэхийг дэмждэггүй"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Байршил идэвхтэй"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi боломжгүй"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Чухал горим"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Сэрүүлгийг тохируулсан"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Түгжээтэй дэлгэцийг өөрчлөх"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Түгжээтэй дэлгэцийг өөрчлөхийн тулд түгжээг тайлна уу"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi боломжгүй байна"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Байршил идэвхтэй"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камерыг блоклосон"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камер болон микрофоныг блоклосон"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофоныг блоклосон"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Гараа ашиглан шилжих"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Товчлуурын шууд холбоосыг мэдэж аваарай"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Мэдрэгч самбараа ашиглан шилжээрэй"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Мэдрэгч самбарын зангаа, товчлуурын шууд холбоос болон бусад зүйлийг мэдэж аваарай"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Буцах зангаа"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Үндсэн нүүрний зангаа"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Саяхны аппуудыг харах"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Болсон"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Буцах"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Буцахын тулд мэдрэгч самбар дээр гурван хуруугаар хүссэн газраа зүүн эсвэл баруун тийш шударна уу.\n\nТа мөн үүнийг хийхэд Action + ESC товчлуурын шууд холбоосыг ашиглах боломжтой."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Үндсэн нүүр лүүгээ хүссэн үедээ очихын тулд дэлгэцийнхээ доод талаас гурван хуруугаараа дээш шударна уу."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Янзтай!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Та үндсэн нүүр лүү очих зангааг гүйцэтгэлээ."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Саяхны аппуудыг харах"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Мэдрэгч самбар дээрээ гурван хуруугаа ашиглан дээш шудраад, удаан дарна уу."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Сайн байна!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Та саяхны аппуудыг харах зангааг гүйцэтгэсэн."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Тусгай товчлуур"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Аппууддаа хандахын тулд гар дээр тань байх тусгай товчлуурыг дарна уу."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Баяр хүргэе!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Гурван хуруугаа ашиглан дээш шудраад, удаан дарна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Бүх аппыг харахын тулд гараа ашиглах"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Тусгай товчлуурыг хүссэн үедээ дарна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Хэт бүүдгэр онцлог одоо гэрэлтүүлгийн гулсуулагчийн нэг хэсэг боллоо"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Та одоо гэрэлтүүлгийн түвшнийг бүр илүү багасгаснаар дэлгэцийг хэт бүүдгэр болгох боломжтой.\n\nЭнэ онцлог нь одоо гэрэлтүүлгийн гулсуулагчийн нэг хэсэг болсон тул Хэт бүүдгэр онцлогийн тохиргоог хасаж байна."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Хэт бүүдгэр онцлогийн товчлолыг хасах"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Хэт бүүдгэр онцлогийн товчлолыг хассан"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Холболт"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Хандалт"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Хэрэгсэл"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 0f3b051..e4063f6 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पीकर आणि डिस्प्ले"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"सुचवलेली डिव्हाइस"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"इनपुट"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"आउटपुट"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"मीडिया दुसऱ्या डिव्हाइसवर शेअर करण्यासाठी तुमचे शेअर केलेले सेशन थांबवा"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"थांबवा"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ब्रॉडकास्टिंग कसे काम करते"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"जोडा"</string>
     <string name="manage_users" msgid="1823875311934643849">"वापरकर्ते व्यवस्‍थापित करा"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ही सूचना स्प्लिट स्क्रीनवर ड्रॅग करण्याला सपोर्ट करत नाही"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"स्थान अ‍ॅक्टिव्ह आहे"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"वाय-फाय उपलब्ध नाही"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"प्राधान्य मोड"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट केला"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"कस्टमाइझ लॉक स्‍क्रीन"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"लॉक स्‍क्रीन कस्टमाइझ करण्यासाठी अनलॉक करा"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"वाय-फाय उपलब्ध नाही"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"स्थान अ‍ॅक्टिव्ह आहे"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"कॅमेरा ब्लॉक केला"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"कॅमेरा आणि मायक्रोफोन ब्लॉक केले आहेत"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"मायक्रोफोन ब्लॉक केला"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"तुमचा कीबोर्ड वापरून नेव्हिगेट करा"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट जाणून घ्या"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"तुमचा टचपॅड वापरून नेव्हिगेट करा"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपॅड जेश्चर, कीबोर्ड शॉर्टकट आणि आणखी बरेच काही जाणून घ्या"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"मागे जा जेश्चर"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम जेश्चर"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"अलीकडील अ‍ॅप्स पहा"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"पूर्ण झाले"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"मागे जा"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"मागे जाण्यासाठी, तीन बोटांनी टचपॅडवर कुठेही डावीकडे किंवा उजवीकडे स्वाइप करा.\n\nतुम्ही यासाठी Action + ESC हा कीबोर्ड शॉर्टकटदेखील वापरू शकता."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"कधीही तुमच्या होम स्क्रीनवर जाण्यासाठी, तीन बोटांनी तुमच्या स्क्रीनच्या तळापासून स्वाइप करा."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"छान!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"तुम्ही गो होम जेश्चर पूर्ण केले आहे."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"अलीकडील अ‍ॅप्स पहा"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"तुमच्या टचपॅडवर तीन बोटांनी वरती आणि खाली स्वाइप करा."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"उत्तम कामगिरी!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तुम्ही अलीकडील ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"अ‍ॅक्शन की"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"तुमची ॲप्स अ‍ॅक्सेस करण्यासाठी, तुमच्या कीबोर्डवरील अ‍ॅक्शन की प्रेस करा."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"अभिनंदन!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन बोटांनी वरती आणि खाली स्वाइप करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सर्व ॲप्स पाहण्यासाठी तुमचा कीबोर्ड वापरा"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"अ‍ॅक्शन की कधीही प्रेस करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"आणखी डिम हे आता ब्राइटनेस स्लायडरमध्ये समाविष्ट आहे"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"तुम्ही आता ब्राइटनेसची पातळी आणखी कमी करून स्क्रीनला आणखी डिम करू शकता.\n\nहे वैशिष्ट्य आता ब्राइटनेसच्या स्लायडरमध्ये समाविष्ट असल्याने, आणखी डिम शॉर्टकट काढून टाकले जात आहेत."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"आणखी डिम शॉर्टकट काढून टाका"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"आणखी डिम शॉर्टकट काढून टाकले आहेत"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"कनेक्टिव्हिटी"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"अ‍ॅक्सेसिबिलिटी"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"उपयुक्तता"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 38434c5..710d342 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Pembesar Suara &amp; Paparan"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Peranti yang Dicadangkan"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Output"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Hentikan sesi dikongsi anda untuk mengalihkan media kepada peranti yang lain"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Berhenti"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cara siaran berfungsi"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Tambah"</string>
     <string name="manage_users" msgid="1823875311934643849">"Urus pengguna"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Pemberitahuan ini tidak menyokong penyeretan kepada skrin pisah"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokasi aktif"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi dimatikan"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Mod keutamaan"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Penggera ditetapkan"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Sesuaikan skrin kunci"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Buka kunci untuk menyesuaikan skrin kunci"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi tidak tersedia"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokasi aktif"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera disekat"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera dan mikrofon disekat"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon disekat"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigasi menggunakan papan kekunci anda"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ketahui pintasan papan kekunci"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigasi menggunakan pad sentuh anda"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Ketahui gerak isyarat pad sentuh, pintasan papan kekunci dan pelbagai lagi"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gerak isyarat kembali"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gerak isyarat pergi ke laman utama"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat apl terbaharu"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Untuk kembali, leret ke kiri atau ke kanan menggunakan tiga jari di mana-mana sahaja pada pad sentuh.\n\nAnda juga boleh menggunakan pintasan papan kekunci Action + ESC untuk kembali."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Untuk mengakses skrin utama anda pada bila-bila masa, leret ke atas menggunakan tiga jari daripada bahagian bawah skrin anda."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bagus!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Anda telah melengkapkan gerak isyarat akses laman utama."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Lihat apl terbaharu"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Leret ke atas dan tahan menggunakan tiga jari pada pad sentuh anda."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Syabas!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda melengkapkan gerak isyarat lihat apl terbaharu."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Kekunci tindakan"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Untuk mengakses semua apl anda, tekan kekunci tindakan pada papan kekunci anda."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tahniah!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Leret ke atas, tahan dengan tiga jari. Ketik untuk mengetahui lebih lanjut tentang gerak isyarat."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gunakan papan kekunci anda untuk melihat semua apl"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tekan kekunci tindakan pada bila-bila masa. Ketik dan ketahui lebih lanjut tentang gerak isyarat."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Kini ciri amat malap merupakan sebahagian daripada peluncur kecerahan"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Kini anda boleh menjadikan skrin amat malap dengan merendahkan lebih lagi tahap kecerahan.\n\nMemandangkan ciri ini kini merupakan sebahagian daripada peluncur kecerahan, pintasan amat malap dialih keluar."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Alih keluar pintasan amat malap"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Pintasan amat malap dialih keluar"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Kesambungan"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Kebolehaksesan"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utiliti"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index ef4b04d..0c9bbe6 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -442,7 +442,7 @@
     <string name="zen_mode_off" msgid="1736604456618147306">"ပိတ်"</string>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"စနစ်ထည့်သွင်းရန်"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ဆက်တင်များတွင် စီမံရန်"</string>
-    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{အသုံးပြုနေသော မုဒ်မရှိပါ}=1{{mode} ကို အသုံးပြုနေသည်}other{မုဒ် # ခုကို အသုံးပြုနေသည်}}"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{သုံးနေသော မုဒ်မရှိပါ}=1{{mode} ကို သုံးနေသည်}other{မုဒ် # ခု သုံးနေသည်}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"နှိုးစက်သံ၊ သတိပေးချက်အသံများ၊ ပွဲစဉ်သတိပေးသံများနှင့် သင်ခွင့်ပြုထားသူများထံမှ ဖုန်းခေါ်မှုများမှလွဲ၍ အခြားအသံများနှင့် တုန်ခါမှုများက သင့်ကို အနှောင့်အယှက်ပြုမည် မဟုတ်ပါ။ သို့သော်လည်း သီချင်း၊ ဗီဒီယိုနှင့် ဂိမ်းများအပါအဝင် သင်ကရွေးချယ်ဖွင့်ထားသည့် အရာတိုင်း၏ အသံကိုမူ ကြားနေရဆဲဖြစ်ပါလိမ့်မည်။"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"နှိုးစက်သံမှလွဲ၍ အခြားအသံများနှင့် တုန်ခါမှုများက သင့်ကို အနှောင့်အယှက်ပြုမည် မဟုတ်ပါ။ သို့သော်လည်း သီချင်း၊ ဗီဒီယိုနှင့် ဂိမ်းများအပါအဝင် သင်ကရွေးချယ်ဖွင့်ထားသည့် အရာတိုင်း၏ အသံကိုမူ ကြားနေရဆဲဖြစ်ပါလိမ့်မည်။"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"စိတ်ကြိုက် ပြုလုပ်ရန်"</string>
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"အကြောင်းကြားချက် မရှိပါ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"အကြောင်းကြားချက်သစ် မရှိပါ"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"အကြောင်းကြားချက် သတိပေးမှု လျှော့ချခြင်း ဖွင့်ထားသည်"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"အကြောင်းကြားချက်များစွာ တစ်ပြိုင်နက်ရပါက သင့်စက်၏ အသံအတိုးအကျယ်နှင့် သတိပေးချက်ကို ၂ မိနစ်ကြာသည်အထိ အလိုအလျောက်လျှော့ချသည်။"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"အကြောင်းကြားချက်များစွာ တစ်ပြိုင်နက်ရပါက သင့်စက်၏ အသံနှင့် သတိပေးချက်ကို ၂ မိနစ်ကြာသည်အထိ အလိုအလျောက်လျှော့ချသည်။"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ပိတ်ရန်"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"အကြောင်းကြားချက်ဟောင်းကြည့်ရန် လော့ခ်ဖွင့်ပါ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ဤစက်ပစ္စည်းကို သင့်မိဘက စီမံခန့်ခွဲသည်"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"စပီကာနှင့် ဖန်သားပြင်များ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"အကြံပြုထားသော စက်ပစ္စည်းများ"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"အခြားစက်သို့ မီဒီယာရွှေ့ပြောင်းရန် သင်၏မျှဝေထားသောစက်ရှင်ကို ရပ်ပါ"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"ရပ်ရန်"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ထုတ်လွှင့်မှုဆောင်ရွက်ပုံ"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"ထည့်ရန်"</string>
     <string name="manage_users" msgid="1823875311934643849">"အသုံးပြုသူများ စီမံရန်"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ဤအကြောင်းကြားချက်သည် ‘မျက်နှာပြင် ခွဲ၍ပြသခြင်း’ သို့ ဖိဆွဲမှုကို မပံ့ပိုးပါ"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"တည်နေရာ ဖွင့်ထားသည်"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi မရပါ"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ဦးစားပေးမုဒ်"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"နိုးစက် သတ်မှတ်ထားသည်"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"လော့ခ်မျက်နှာပြင်စိတ်ကြိုက်လုပ်ရန်"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"လော့ခ်မျက်နှာပြင် စိတ်ကြိုက်လုပ်ရန် ဖွင့်ပါ"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi မရနိုင်ပါ"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"တည်နေရာ ဖွင့်ထားသည်"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ကင်မရာကို ပိတ်ထားသည်"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ကင်မရာနှင့် မိုက်ခရိုဖုန်းကို ပိတ်ထားသည်"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"မိုက်ခရိုဖုန်းကို ပိတ်ထားသည်"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"သင့်ကီးဘုတ်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"လက်ကွက်ဖြတ်လမ်းများကို လေ့လာပါ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"သင့်တာ့ချ်ပက်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"တာ့ချ်ပက်လက်ဟန်များ၊ လက်ကွက်ဖြတ်လမ်းများ စသည်တို့ကို လေ့လာပါ"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"နောက်သို့ လက်ဟန်"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ပင်မစာမျက်နှာ လက်ဟန်"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"မကြာသေးမီကအက်ပ်များကို ကြည့်ရန်"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ပြီးပြီ"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ပြန်သွားရန်"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"နောက်ပြန်သွားရန် တာ့ချ်ပက်ပေါ်ရှိ မည်သည့်နေရာ၌မဆို လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ။\n\n၎င်းအတွက် လက်ကွက်ဖြတ်လမ်း Action + ESC ကိုလည်း သုံးနိုင်သည်။"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ပင်မစာမျက်နှာသို့ အချိန်မရွေးသွားရန် စခရင်အောက်ခြေမှ အပေါ်သို့ လက်သုံးချောင်းဖြင့် ပွတ်ဆွဲပါ။"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ကောင်းသည်။"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် အပြီးသတ်လိုက်ပါပြီ။"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်း"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"သင့်တာ့ချ်ပက်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ။"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"တော်ပါပေသည်။"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ။"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"လုပ်ဆောင်ချက်ကီး"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"သင့်အက်ပ်များသုံးရန် ကီးဘုတ်ပေါ်ရှိ လုပ်ဆောင်ချက်ကီးကို နှိပ်ပါ။"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ဂုဏ်ယူပါသည်။"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"အက်ပ်အားလုံးကြည့်ရန် သင့်ကီးဘုတ်ကို သုံးပါ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"လုပ်ဆောင်ချက်ကီးကို အချိန်မရွေးနှိပ်ပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ပိုမှိန်ခြင်းသည် တောက်ပမှုရွှေ့တုံးတွင် ပါဝင်လာပြီ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"တောက်ပမှုအဆင့် ထပ်မံလျှော့ချခြင်းဖြင့် စခရင်ကို ပိုမှိန်အောင် လုပ်နိုင်ပါပြီ။\n\nဤတူးလ်သည် တောက်ပမှုရွှေ့တုံးတွင် ပါဝင်လာသဖြင့် ပိုမှိန်ခြင်းဖြတ်လမ်းများကို ဖယ်ရှားထားသည်။"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"ပိုမှိန်ခြင်း ဖြတ်လမ်းများ ဖယ်ရှားရန်"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"ပိုမှိန်ခြင်း ဖြတ်လမ်းများကို ဖယ်ရှားထားသည်"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"ချိတ်ဆက်နိုင်မှု"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"အများသုံးနိုင်မှု"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"အထောက်အကူပြု ဆော့ဖ်ဝဲလ်များ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 55535dd..a097505 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Høyttalere og skjermer"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Foreslåtte enheter"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stopp den delte økten for å flytte medieinnholdet til en annen enhet"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stopp"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Slik fungerer kringkasting"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Legg til"</string>
     <string name="manage_users" msgid="1823875311934643849">"Brukervalg"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Dette varselet støtter ikke at du drar det til en delt skjerm"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Posisjon er aktiv"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi er utilgjengelig"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioriteringsmodus"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmen er stilt inn"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Tilpass låseskjermen"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Du må låse opp enheten for å tilpasse låseskjermen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wifi er ikke tilgjengelig"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Posisjon er aktiv"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kameraet er blokkert"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kameraet og mikrofonen er blokkert"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofonen er blokkert"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger med tastaturet"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lær deg hurtigtaster"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger med styreflaten"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Lær deg styreflatebevegelser, hurtigtaster med mer"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tilbakebevegelse"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Startskjermbevegelse"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se nylige apper"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Ferdig"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbake"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"For å gå tilbake, sveip mot høyre eller venstre med tre fingre hvor som helst på styreflaten.\n\nDu kan også gjøre dette med hurtigtasten Action + Esc."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"For å gå til startskjermen, sveip opp med tre fingre fra bunnen av skjermen når som helst."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bra!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Du har fullført bevegelsen for å gå til startskjermen."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Se nylige apper"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Sveip opp og hold med tre fingre på styreflaten."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bra jobbet!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har fullført bevegelsen for å se nylige apper."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Handlingstast"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"For å åpne appene dine, trykk på handlingstasten på tastaturet."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulerer!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Sveip opp og hold med tre fingre. Trykk for å lære flere bevegelser."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Bruk tastaturet for å se alle apper"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Trykk på handlingstasten når som helst. Trykk for å lære flere bevegelser."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Nå er ekstra dimming en del av glidebryteren for lysstyrke"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Nå kan du gjøre skjermen ekstra dimmet ved å redusere lysstyrken enda mer.\n\nSiden denne funksjonen nå er en del av glidebryteren for lysstyrke, fjernes snarveiene for ekstra dimming."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Fjern snarveiene for ekstra dimming"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Snarveiene for ekstra dimming er fjernet"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Tilkobling"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Tilgjengelighet"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Systemverktøy"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 68cca07..e2ae7b9 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -442,7 +442,7 @@
     <string name="zen_mode_off" msgid="1736604456618147306">"अफ छ"</string>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"सेटअप गर्नुहोस्"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिङमा गई व्यवस्थापन गर्नुहोस्"</string>
-    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कुनै पनि मोड सक्रिय छैन}=1{{mode} सक्रिय छ}other{# वटा मोड सक्रिय छन्}}"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कुनै पनि सक्रिय छैन}=1{{mode} सक्रिय छ}other{# मोड सक्रिय छन्}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"तपाईंलाई अलार्म, रिमाइन्डर, कार्यक्रम र तपाईंले निर्दिष्ट गर्नुभएका कलरहरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"तपाईंलाई अलार्महरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">" कस्टम बनाउनुहोस्"</string>
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"स्पिकर तथा डिस्प्लेहरू"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"सिफारिस गरिएका डिभाइसहरू"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"इन्पुट"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"आउटपुट"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"मिडिया अर्को डिभाइसमा सार्नका लागि तपाईंले सेयर गरेको सत्र अन्त्य गर्नुहोस्"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"रोक्नुहोस्"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"प्रसारण गर्ने सुविधाले कसरी काम गर्छ"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"हाल्नुहोस्"</string>
     <string name="manage_users" msgid="1823875311934643849">"प्रयोगकर्ताहरूको व्यवस्थापन गर्नुहोस्"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"यो सूचना ड्र्याग गरेर स्प्लिट स्क्रिनमा लैजान मिल्दैन"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"लोकेसन सक्रिय छ"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi उपलब्ध छैन"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"प्राथमिकता मोड"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट गरिएको छ"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"लक स्क्रिन कस्टमाइज गर्नुहोस्"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"लक स्क्रिन कस्टमाइज गर्न अनलक गर्नुहोस्"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi उपलब्ध छैन"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"लोकेसन सक्रिय छ"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"क्यामेरा ब्लक गरिएको छ"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"क्यामेरा र माइक्रोफोन ब्लक गरिएको छ"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"माइक्रोफोन ब्लक गरिएको छ"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"किबोर्ड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"किबोर्डका सर्टकटहरू प्रयोग गर्न सिक्नुहोस्"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचप्याड जेस्चर, किबोर्डका सर्टकट र अन्य कुरा प्रयोग गर्न सिक्नुहोस्"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ब्याक जेस्चर"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम जेस्चर"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"सम्पन्न भयो"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"पछाडि जान तीन वटा औँलाले टचप्याडमा कतै छोएर बायाँ वा दायाँतिर स्वाइप गर्नुहोस्।\n\nतपाईं यसका लागि किबोर्डको सर्टकट \"Action + ESC\" पनि प्रयोग गर्न सक्नुहुन्छ।"</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"जुनसुकै बेला आफ्नो होम स्क्रिनमा जान स्क्रिनको फेदबाट तीन वटा औँलाले माथितिर स्वाइप गर्नुहोस्।"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"राम्रो!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो।"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"तीन वटा औँला प्रयोग गरी टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्।"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"अद्भुत!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले हालसालै चलाइएका एपहरू हेर्ने जेस्चर पूरा गर्नुभएको छ।"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"एक्सन की"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"आफ्ना एपहरू एक्सेस गर्न आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्।"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"बधाई छ!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"तिन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सबै एपहरू हेर्न आफ्नो किबोर्ड प्रयोग गर्नुहोस्"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"जुनसुकै बेला एक्सन की थिच्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"\"अझै मधुरो\" सुविधा अब चमक घटबढ गर्ने स्लाइडरमा समावेश गरिएको छ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"तपाईं चमकको स्तर अझ बढी घटाएर स्क्रिन अझै मधुरो बनाउन सक्नुहुन्छ।\n\n\"अझै मधुरो\" सुविधा अब चमक घटबढ गर्ने स्लाइडरमा समावेश गरिएकाले यो सुविधाका सर्टकर्टहरू हटाइँदै छन्।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"\"अझै मधुरो\" सुविधाका सर्टकटहरू हटाउनुहोस्"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"\"अझै मधुरो\" सुविधाका सर्टकटहरू हटाइएका छन्"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"कनेक्टिभिटी"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"सर्वसुलभता"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"युटिलिटी"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 2e9fc8e..9681f1e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Geen meldingen"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nieuwe meldingen"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Afkoelperiode van meldingen staat aan"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Je apparaatvolume en meldingen worden automatisch maximaal 2 minuten beperkt als je te veel meldingen tegelijk krijgt."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Als je te veel meldingen tegelijk krijgt, worden het volume op je apparaat en meldingen automatisch maximaal 2 minuten beperkt."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Uitzetten"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontgrendel om oudere meldingen te zien"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dit apparaat wordt beheerd door je ouder"</string>
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers en schermen"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Voorgestelde apparaten"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Invoer"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Uitvoer"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop je gedeelde sessie om media naar een ander apparaat te verplaatsen"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stoppen"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Hoe uitzenden werkt"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Toevoegen"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gebruikers beheren"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Deze melding biedt geen ondersteuning voor slepen naar het gesplitste scherm"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Locatie actief"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wifi niet beschikbaar"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioriteitsmodus"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wekker gezet"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Vergrendelscherm aanpassen"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Ontgrendelen om het vergrendelscherm aan te passen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wifi niet beschikbaar"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Locatie actief"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Camera geblokkeerd"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Camera en microfoon geblokkeerd"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microfoon geblokkeerd"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeren met je toetsenbord"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer sneltoetsen die je kunt gebruiken"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeren met je touchpad"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer meer over onder andere touchpadgebaren en sneltoetsen"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gebaar voor terug"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gebaar voor startscherm"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Recente apps bekijken"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Terug"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Als je wilt teruggaan, swipe je met 3 vingers naar links of rechts op de touchpad.\n\nJe kunt hiervoor ook de sneltoets Actie + ESC gebruiken."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Swipe met 3 vingers omhoog vanaf de onderkant van het scherm om naar het startscherm te gaan."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Mooi zo!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Je weet nu hoe je het gebaar Naar startscherm maakt."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Recente apps bekijken"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Swipe met 3 vingers omhoog en houd vast op je touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed werk!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Je weet nu hoe je het gebaar Recente apps bekijken maakt."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Actietoets"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Als je toegang tot je apps wilt krijgen, druk je op de actietoets op je toetsenbord."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gefeliciteerd!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe met 3 vingers omhoog en houd vast. Tik voor meer gebaren."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Je toetsenbord gebruiken om alle apps te bekijken"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Druk op de actietoets wanneer je wilt. Tik voor meer gebaren."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Extra dimmen maakt nu deel uit van de schuifregelaar voor helderheid"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Je kunt het scherm nu extra dimmen door het helderheidsniveau nog verder te verlagen.\n\nOmdat deze functie nu deel uitmaakt van de schuifregelaar voor helderheid, worden snelkoppelingen voor extra dimmen verwijderd."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Snelkoppelingen voor extra dimmen verwijderen"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Snelkoppelingen voor extra dimmen verwijderd"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectiviteit"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Toegankelijkheid"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Hulpprogramma\'s"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 7eced26..14e8490 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେଗୁଡ଼ିକ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ପ୍ରସ୍ତାବିତ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"ଅନ୍ୟ ଏକ ଡିଭାଇସକୁ ମିଡିଆ ମୁଭ କରିବା ପାଇଁ ଆପଣଙ୍କ ସେୟାର କରାଯାଇଥିବା ସେସନକୁ ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ବ୍ରଡକାଷ୍ଟିଂ କିପରି କାମ କରେ"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"ଯୋଗ କରନ୍ତୁ"</string>
     <string name="manage_users" msgid="1823875311934643849">"ୟୁଜରମାନଙ୍କୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ଏହି ବିଜ୍ଞପ୍ତି ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ଟାଣିବାକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ଲୋକେସନ ସକ୍ରିୟ ଅଛି"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"ୱାଇ-ଫାଇ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ପ୍ରାଥମିକତା ମୋଡ"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ଆଲାରାମ ସେଟ"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ଲକ ସ୍କ୍ରିନକୁ କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ଲକ ସ୍କ୍ରିନକୁ କଷ୍ଟମାଇଜ କରିବା ପାଇଁ ଅନଲକ କରନ୍ତୁ"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"ୱାଇ-ଫାଇ ଉପଲବ୍ଧ ନାହିଁ"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ଲୋକେସନ ସକ୍ରିୟ ଅଛି"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"କେମେରାକୁ ବ୍ଲକ କରାଯାଇଛି"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"କେମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ବ୍ଲକ କରାଯାଇଛି"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ମାଇକ୍ରୋଫୋନକୁ ବ୍ଲକ କରାଯାଇଛି"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ଆପଣଙ୍କ କୀବୋର୍ଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ଟଚପେଡ ଜେଶ୍ଚର, କୀବୋର୍ଡ ସର୍ଟକଟ ଏବଂ ଆହୁରି ଅନେକ କିଛି ବିଷୟରେ ଜାଣନ୍ତୁ"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ବେକ ଜେଶ୍ଚର"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ହୋମ ଜେଶ୍ଚର"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ହୋଇଗଲା"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ପଛକୁ ଫେରନ୍ତୁ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ପଛକୁ ଫେରିବା ପାଇଁ ଯେ କୌଣସି ସ୍ଥାନରେ ତିନି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ବାମ କିମ୍ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ।\n\nଏଥିପାଇଁ ଆପଣ କୀବୋର୍ଡ ସର୍ଟକଟ ଆକ୍ସନ + ESC ମଧ୍ୟ ବ୍ୟବହାର କରିପାରିବେ।"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ଯେ କୌଣସି ସମୟରେ ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନକୁ ଯିବା ପାଇଁ ଆପଣଙ୍କ ସ୍କିନର ତଳୁ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ।"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ବଢ଼ିଆ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ଆପଣ \'ହୋମକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିକୁ ବ୍ୟବହାର କରି ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ।"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ବଢ଼ିଆ କାମ!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ଆପଣ ବର୍ତ୍ତମାନର ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ଆକ୍ସନ କୀ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ଆପଣଙ୍କ ଆପ୍ସ ଆକ୍ସେସ କରିବା ପାଇଁ ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ।"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ଅଭିନନ୍ଦନ!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ସମସ୍ତ ଆପ୍ସ ଭ୍ୟୁ କରିବା ପାଇଁ ଆପଣଙ୍କ କୀବୋର୍ଡକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ଯେ କୌଣସି ସମୟରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ଅତିରିକ୍ତ ଡିମ ବର୍ତ୍ତମାନ ଉଜ୍ଜ୍ୱଳତା ସ୍ଲାଇଡରର ଅଂଶ ଅଟେ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ବର୍ତ୍ତମାନ ଆପଣ ଉଜ୍ଜ୍ୱଳତାର ଲେଭେଲକୁ ଆହୁରି କମ କରି ସ୍କ୍ରିନକୁ ଅତିରିକ୍ତ ଡିମ କରିପାରିବେ।\n\nଏହି ଫିଚର ବର୍ତ୍ତମାନ ଉଜ୍ଜ୍ୱଳତା ସ୍ଲାଇଡରର ଅଂଶ ହୋଇଥିବା ଯୋଗୁଁ ଅତିରିକ୍ତ ଡିମ ସର୍ଟକଟଗୁଡ଼ିକୁ କାଢ଼ି ଦିଆଯାଉଛି।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"ଅତିରିକ୍ତ ଡିମ ସର୍ଟକଟକୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"ଅତିରିକ୍ତ ଡିମ ସର୍ଟକଟକୁ କାଢ଼ି ଦିଆଯାଇଛି"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"କନେକ୍ଟିଭିଟି"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ଆକ୍ସେସିବିଲିଟୀ"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ୟୁଟିଲିଟି"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 6a46613..8e7ed22e 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"ਕੋਈ ਨਵੀਂ ਸੂਚਨਾ ਨਹੀਂ ਹੈ"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"ਨੋਟੀਫ਼ਿਕੇਸ਼ਨ ਕੂਲਡਾਊਨ ਚਾਲੂ ਹੈ"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ਇੱਕ ਵਾਰ \'ਚ ਕਈ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ, ਡੀਵਾਈਸ ਦੀ ਅਵਾਜ਼ ਤੇ ਅਲਰਟ ਵੱਧੋ-ਵੱਧ 2 ਮਿੰਟਾਂ ਲਈ ਆਪਣੇ ਆਪ ਘਟ ਹੋ ਜਾਂਦੇ ਹਨ।"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ਇੱਕ ਵਾਰ \'ਚ ਕਈ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ, ਡੀਵਾਈਸ ਦੀ ਅਵਾਜ਼ ਅਤੇ ਅਲਰਟ ਵੱਧੋ-ਵੱਧ 2 ਮਿੰਟਾਂ ਲਈ ਆਪਣੇ-ਆਪ ਘੱਟ ਜਾਂਦੇ ਹਨ।"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ਬੰਦ ਕਰੋ"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ਪੁਰਾਣੀਆਂ ਸੂਚਨਾਵਾਂ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ਸਪੀਕਰ ਅਤੇ ਡਿਸਪਲੇਆਂ"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ਸੁਝਾਏ ਗਏ ਡੀਵਾਈਸ"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"ਮੀਡੀਆ ਨੂੰ ਕਿਸੇ ਹੋਰ ਡੀਵਾਈਸ \'ਤੇ ਲਿਜਾਉਣ ਲਈ ਆਪਣੇ ਸਾਂਝੇ ਕੀਤੇ ਸੈਸ਼ਨ ਨੂੰ ਬੰਦ ਕਰੋ"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"ਬੰਦ ਕਰੋ"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ਪ੍ਰਸਾਰਨ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="manage_users" msgid="1823875311934643849">"ਵਰਤੋਂਕਾਰਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ਇਹ ਸੂਚਨਾ ਸਪਲਿਟ ਸਕ੍ਰੀਨ \'ਤੇ ਘਸੀਟਣ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ ਹੈ"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ਟਿਕਾਣਾ ਕਿਰਿਆਸ਼ੀਲ ਹੈ"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"ਵਾਈ-ਫਾਈ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ਤਰਜੀਹੀ ਮੋਡ"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ਅਲਾਰਮ ਸੈੱਟ ਹੈ"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ਲਾਕ ਸਕ੍ਰੀਨ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ਲਾਕ ਸਕ੍ਰੀਨ ਨੂੰ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"ਵਾਈ-ਫਾਈ ਉਪਲਬਧ ਨਹੀਂ"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ਟਿਕਾਣਾ ਕਿਰਿਆਸ਼ੀਲ ਹੈ"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"ਕੈਮਰਾ ਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਲਾਕ ਕੀਤੇ ਗਏ"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਬਾਰੇ ਜਾਣੋ"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ਟੱਚਪੈਡ ਇਸ਼ਾਰੇ, ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਬਾਰੇ ਜਾਣੋ"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ਪਿੱਛੇ ਜਾਣ ਦਾ ਇਸ਼ਾਰਾ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ਹੋਮ \'ਤੇ ਜਾਣ ਦਾ ਇਸ਼ਾਰਾ"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ਹੋ ਗਿਆ"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ਵਾਪਸ ਜਾਓ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ਵਾਪਸ ਜਾਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਕਿਤੇ ਵੀ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।\n\nਤੁਸੀਂ ਇਸ ਲਈ ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ Action + ESC ਦੀ ਵਰਤੋਂ ਵੀ ਕਰ ਸਕਦੇ ਹੋ।"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ਕਿਸੇ ਵੀ ਸਮੇਂ ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਜਾਣ ਲਈ, ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ਵਧੀਆ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ।"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ਬਹੁਤ ਵਧੀਆ!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ਤੁਸੀਂ \'ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ।"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ਕਾਰਵਾਈ ਕੁੰਜੀ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ਆਪਣੀਆਂ ਐਪਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਲਈ, ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ।"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ਵਧਾਈਆਂ!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ਸਾਰੀਆਂ ਐਪਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਵਰਤੋ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ਕਿਸੇ ਵੀ ਸਮੇਂ ਕਾਰਵਾਈ ਕੁੰਜੀ ਦਬਾਓ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"\'ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ\' ਹੁਣ ਚਮਕ ਸਲਾਈਡਰ ਦਾ ਹਿੱਸਾ ਹੈ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ਤੁਸੀਂ ਹੁਣ ਚਕਮ ਦੇ ਪੱਧਰ ਨੂੰ ਹੋਰ ਵੀ ਘੱਟ ਕਰ ਕੇ ਸਕ੍ਰੀਨ ਦੀ ਚਮਕ ਨੂੰ ਜ਼ਿਆਦਾ ਘੱਟ ਕਰ ਸਕਦੇ ਹੋ।\n\nਕਿਉਂਕਿ ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਹੁਣ ਚਮਕ ਸਲਾਈਡਰ ਦਾ ਹਿੱਸਾ ਹੈ, \'ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ\' ਸ਼ਾਰਟਕੱਟ ਹਟਾਏ ਜਾ ਰਹੇ ਹਨ।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"\'ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ\' ਸ਼ਾਰਟਕੱਟ ਹਟਾਓ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"\'ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ\' ਸ਼ਾਰਟਕੱਟ ਹਟਾਏ ਗਏ"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"ਕਨੈਕਟੀਵਿਟੀ"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ਪਹੁੰਚਯੋਗਤਾ"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ਉਪਯੋਗਤਾਵਾਂ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 770d995..287ecd3 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Brak powiadomień"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Brak nowych powiadomień"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Wyciszanie powiadomień jest włączone"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Gdy otrzymasz za dużo powiadomień, dźwięk i alerty zostaną automatycznie wyciszone na maks. 2 min."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Gdy w krótkim czasie otrzymasz za dużo powiadomień, dźwięki zostaną automatycznie wyciszone na maks. 2 min."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Wyłącz"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odblokuj i zobacz starsze powiadomienia"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Tym urządzeniem zarządza Twój rodzic"</string>
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Głośniki i wyświetlacze"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Proponowane urządzenia"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Urządzenie wejściowe"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Urządzenie wyjściowe"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Zatrzymaj udostępnianie sesji, aby przenieść multimedia na inne urządzenie"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Zatrzymaj"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jak działa transmitowanie"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Dodaj"</string>
     <string name="manage_users" msgid="1823875311934643849">"Zarządzaj użytkownikami"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"To powiadomienie nie obsługuje dzielenia ekranu przez przeciąganie."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokalizacja jest aktywna"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Sieć Wi‑Fi niedostępna"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Tryb priorytetowy"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm ustawiony"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Dostosuj ekran blokady"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Odblokuj, aby dostosować ekran blokady"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Sieć Wi-Fi jest niedostępna"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokalizacja jest aktywna"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera jest zablokowana"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera i mikrofon są zablokowane"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon jest zablokowany"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nawiguj za pomocą klawiatury"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Dowiedz się więcej o skrótach klawiszowych"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nawiguj za pomocą touchpada"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Poznaj gesty na touchpada, skróty klawiszowe i inne funkcje"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gest przejścia wstecz"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gest przejścia na ekran główny"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Wyświetlanie ostatnich aplikacji"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotowe"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Wróć"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Aby przejść wstecz, przesuń 3 palcami w lewo lub w prawo w dowolnym miejscu touchpada.\n\nMożesz też użyć do tego skrótu klawiszowego Action + ESC."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Aby w dowolnym momencie wyświetlić ekran główny, przesuń od dołu ekranu w górę 3 palcami."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Super!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Gest przechodzenia na ekran główny został opanowany."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Wyświetlanie ostatnich aplikacji"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Przesuń w górę za pomocą 3 palców na touchpadzie i przytrzymaj."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Brawo!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Znasz już gest wyświetlania ostatnio używanych aplikacji."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Klawisz działania"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Aby uzyskać dostęp do aplikacji, naciśnij klawisz działania na klawiaturze."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulacje!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Przesuń w górę za pomocą 3 palców i przytrzymaj. Kliknij, aby poznać więcej gestów."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Wyświetlanie wszystkich aplikacji za pomocą klawiatury"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Naciśnij klawisz działania w dowolnym momencie. Kliknij, aby poznać więcej gestów."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Dodatkowe przyciemnienie jest teraz częścią suwaka jasności"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Możesz teraz dodatkowo przyciemnić ekran, jeszcze bardziej zmniejszając poziom jasności.\n\nTa funkcja jest teraz częścią suwaka jasności, więc skróty do dodatkowego przyciemniania zostaną usunięte."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Usuń skróty do dodatkowego przyciemnienia"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Skróty do dodatkowego przyciemnienia zostały usunięte"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Łączność"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Ułatwienia dostępu"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Narzędzia"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 0563454..408ffcb 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Saída"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Interrompa sua sessão compartilhada para transferir mídia a outro dispositivo"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Parar"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Adicionar"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gerenciar usuários"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Esta notificação não pode ser arrastada para a tela dividida"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Localização ativa"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi-Fi indisponível"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modo de prioridade"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizar a tela de bloqueio"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Desbloqueie para personalizar a tela de bloqueio"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi indisponível"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Localização ativa"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Câmara bloqueada"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Câmera e microfone bloqueados"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microfone bloqueado"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto de volta"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto de início"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad.\n\nVocê também pode usar o atalho de teclado Ação + ESC."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para acessar sua tela inicial a qualquer momento, deslize de baixo para cima na tela com três dedos."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Legal!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Você concluiu o gesto para acessar a tela inicial."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver os apps recentes"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Deslize para cima e pressione com 3 dedos no touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de ação"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acessar os apps, pressione a tecla de ação no teclado."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize para cima e pressione com três dedos. Toque para aprender outros gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todos os apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pressione a tecla de ação a qualquer momento. Toque para aprender outros gestos."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"O recurso Escurecer a tela agora faz parte do controle deslizante de brilho"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Agora, é possível usar o recurso Escurecer a tela mostrado na parte de cima, que diminui ainda mais o nível de brilho.\n\nComo esse recurso agora faz parte do controle deslizante de brilho, os atalhos de Escurecer a tela estão sendo removidos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Remover atalhos de Escurecer a tela"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Atalhos de Escurecer a tela removidos"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Conectividade"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Acessibilidade"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitários"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 0b31bab..2e0bb56 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altifalantes e ecrãs"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Pare a sua sessão partilhada para mover conteúdos multimédia para outro dispositivo"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Parar"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Adicionar"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gerir utilizadores"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Esta notificação não pode ser arrastada para o ecrã dividido"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Localização ativa"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi indisponível"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modo Prioridade"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizar ecrã de bloqueio"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Desbloqueie para personalizar o ecrã de bloqueio"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi indisponível"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Localização ativa"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Câmara bloqueada"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Câmara e microfone bloqueados"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microfone bloqueado"</string>
@@ -1396,6 +1398,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Indicador para arrastar"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue com o teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos de teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue com o touchpad"</string>
@@ -1404,8 +1407,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos de teclado e muito mais"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto para retroceder"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para aceder ao ecrã principal"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluir"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para retroceder, deslize rapidamente para a esquerda ou direita com 3 dedos em qualquer parte do touchpad.\n\nPara o fazer, também pode usar o atalho de teclado Ação + ESC."</string>
@@ -1415,14 +1417,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para aceder ao ecrã principal em qualquer altura, deslize rapidamente com 3 dedos de baixo para cima no ecrã."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Boa!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Concluiu o gesto para aceder ao ecrã principal."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver apps recentes"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Deslize rapidamente para cima e mantenha premido com 3 dedos no touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Concluiu o gesto para ver as apps recentes."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de ação"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para aceder às suas apps, prima a tecla de ação no teclado."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
@@ -1446,14 +1444,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize rapidamente para cima e mantenha premido com 3 dedos. Toque para aprender mais gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todas as apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Prima a tecla de ação em qualquer altura. Toque para aprender mais gestos."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Agora, o escurecimento extra faz parte do controlo de deslize"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Agora, pode tornar o ecrã ainda mais escuro reduzindo ainda mais o nível de brilho.\n\nUma vez que esta funcionalidade faz agora parte do controlo de deslize do brilho, os atalhos de escurecimento extra vão ser removidos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Remover atalhos de escurecimento extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Atalhos de escurecimento extra removidos"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Conetividade"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Acessibilidade"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitários"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 0563454..408ffcb 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Entrada"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Saída"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Interrompa sua sessão compartilhada para transferir mídia a outro dispositivo"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Parar"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Adicionar"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gerenciar usuários"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Esta notificação não pode ser arrastada para a tela dividida"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Localização ativa"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi-Fi indisponível"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modo de prioridade"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizar a tela de bloqueio"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Desbloqueie para personalizar a tela de bloqueio"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi indisponível"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Localização ativa"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Câmara bloqueada"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Câmera e microfone bloqueados"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microfone bloqueado"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto de volta"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto de início"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad.\n\nVocê também pode usar o atalho de teclado Ação + ESC."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para acessar sua tela inicial a qualquer momento, deslize de baixo para cima na tela com três dedos."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Legal!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Você concluiu o gesto para acessar a tela inicial."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver os apps recentes"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Deslize para cima e pressione com 3 dedos no touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de ação"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acessar os apps, pressione a tecla de ação no teclado."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize para cima e pressione com três dedos. Toque para aprender outros gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todos os apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pressione a tecla de ação a qualquer momento. Toque para aprender outros gestos."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"O recurso Escurecer a tela agora faz parte do controle deslizante de brilho"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Agora, é possível usar o recurso Escurecer a tela mostrado na parte de cima, que diminui ainda mais o nível de brilho.\n\nComo esse recurso agora faz parte do controle deslizante de brilho, os atalhos de Escurecer a tela estão sendo removidos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Remover atalhos de Escurecer a tela"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Atalhos de Escurecer a tela removidos"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Conectividade"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Acessibilidade"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitários"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 5edcaf1..443d28a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și ecrane"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispozitive sugerate"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Oprește sesiunea comună ca să muți elementul media pe alt dispozitiv"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Oprește"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cum funcționează transmisia"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Adaugă"</string>
     <string name="manage_users" msgid="1823875311934643849">"Gestionează"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Notificarea nu acceptă tragerea pe ecranul împărțit"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Locație activă"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi indisponibil"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modul Prioritate"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmă setată"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizează ecranul de blocare"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Deblochează pentru a personaliza ecranul de blocare"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Conexiune Wi-Fi indisponibilă"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Locație activă"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Camera foto a fost blocată"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Camera foto și microfonul sunt blocate"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Microfonul a fost blocat"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navighează folosind tastatura"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Învață comenzile rapide de la tastatură"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navighează folosind touchpadul"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Învață gesturi pentru touchpad, comenzi rapide de la tastatură și altele"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gestul Înapoi"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gestul Ecran de pornire"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Vezi aplicațiile recente"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gata"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Înapoi"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pentru a reveni, glisează spre stânga sau spre dreapta cu trei degete oriunde pe touchpad.\n\nPoți folosi și comanda rapidă de la tastatură Action + ESC pentru aceasta."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Pentru a accesa oricând ecranul de pornire, glisează în sus cu trei degete din partea de jos a ecranului"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bravo!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ai finalizat gestul „accesează ecranul de pornire”."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Vezi aplicațiile recente"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Glisează în sus și ține apăsat cu trei degete pe touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Excelent!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ai finalizat gestul pentru afișarea aplicațiilor recente."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tasta de acțiuni"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Pentru a accesa aplicațiile, apasă tasta de acțiuni de pe tastatură."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Felicitări!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Glisează în sus și ține apăsat cu trei degete. Atinge ca să înveți mai multe gesturi."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Folosește-ți tastatura ca să vezi toate aplicațiile"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Apasă oricând tasta de acțiuni. Atinge ca să înveți mai multe gesturi."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Luminozitatea redusă suplimentar face acum parte din glisorul de luminozitate"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Poți reduce suplimentar luminozitatea ecranului dacă scazi nivelul de luminozitate.\n\nÎntrucât această funcție este acum disponibilă în glisorul de luminozitate, comenzile rapide de luminozitate redusă suplimentar sunt eliminate."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Elimină comenzile rapide de luminozitate redusă suplimentar"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"S-au eliminat comenzile rapide de luminozitate redusă suplimentar"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Conectivitate"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accesibilitate"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitare"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a1f1750..66d7808 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки и дисплеи"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Рекомендуемые устройства"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Устройства ввода"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Устройства вывода"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Чтобы перенести медиафайлы на другое устройство, закройте доступ."</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Закрыть"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Как работают трансляции"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Добавить"</string>
     <string name="manage_users" msgid="1823875311934643849">"Управление пользователями"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Это уведомление нельзя перетаскивать между частями разделенного экрана."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Используется геолокация"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Сеть Wi‑Fi недоступна"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Режим приоритета"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будильник установлен"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Настройки заблок. экрана"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Разблокируйте устройство, чтобы настроить заблокированный экран"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Функция Wi-Fi недоступна"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Используется геолокация"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камера заблокирована"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камера и микрофон заблокированы"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофон заблокирован"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигация с помощью клавиатуры"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Узнайте о сочетаниях клавиш."</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигация с помощью сенсорной панели"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Узнайте о жестах на сенсорной панели, сочетаниях клавиш и многом другом."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест \"назад\""</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест \"на главный экран\""</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Жест \"Просмотр недавних приложений\""</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Чтобы вернуться назад, проведите по сенсорной панели тремя пальцами влево или вправо.\n\nВы также можете нажать клавишу действия + Esc."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Чтобы перейти на главный экран, проведите снизу вверх тремя пальцами."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Неплохо!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Вы выполнили жест для перехода на главный экран."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Просмотр недавних приложений"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Проведите вверх по сенсорной панели тремя пальцами и удерживайте."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы выполнили жест для просмотра недавних приложений."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавиша действия"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Чтобы перейти к приложениям, нажмите клавишу действия на клавиатуре."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Готово!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Для этого проведите тремя пальцами вверх и удерживайте. Нажмите, чтобы посмотреть другие жесты."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Открывайте список всех приложений с помощью клавиатуры"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Для этого можно использовать клавишу действия. Нажмите, чтобы посмотреть другие жесты."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Дополнительно уменьшать яркость теперь можно через стандартный ползунок"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Вы можете дополнительно уменьшать яркость экрана, чтобы он был ещё темнее.\n\nТак как теперь это можно делать через стандартный ползунок, быстрые команды для дополнительного уменьшения яркости будут удалены."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Удалить быстрые команды для дополнительного уменьшения яркости"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Быстрые команды для дополнительного уменьшения яркости удалены."</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Подключение"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Специальные возможности"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Утилиты"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 3dac0c5..7dd43a9 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ස්පීකර් සහ සංදර්ශක"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"යෝජිත උපාංග"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ආදානය"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"ප්‍රතිදානය"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"මාධ්‍ය වෙනත් උපාංගයකට ගෙන යාමට ඔබේ බෙදා ගත් සැසිය නවත්වන්න"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"නවත්වන්න"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"විකාශනය ක්‍රියා කරන ආකාරය"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"එක් කරන්න"</string>
     <string name="manage_users" msgid="1823875311934643849">"පරිශීලකයන් කළමනාකරණය කරන්න"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"මෙම දැනුම්දීම බෙදුම් තිරය වෙත ඇද ගෙන යාමට සහාය නොදක්වයි."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ස්ථානය සක්‍රියයි"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi ලබා ගත නොහැකිය"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ප්‍රමුඛතා ප්‍රකාරය"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"සීනුව සකසන ලදි"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"අගුළු තිරය අභිරුචිකරණය කරන්න"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"අගුළු තිරය අභිරුචිකරණය කිරීමට අගුළු හරින්න"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi ලද නොහැක"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ස්ථානය සක්‍රියයි"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"කැමරාව අවහිරයි"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"කැමරාව සහ මයික්‍රොෆෝනය අවහිරයි"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"මයික්‍රොෆෝනය අවහිරයි"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ඔබේ යතුරු පුවරුව භාවිතයෙන් සංචාලනය කරන්න"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"යතුරුපුවරු කෙටිමං ඉගෙන ගන්න"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ඔබේ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ස්පර්ශ පෑඩ් අභිනයන්, යතුරුපුවරු කෙටිමං සහ තවත් දේ ඉගෙන ගන්න"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ආපසු අභිනය"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"නිවෙස් අභිනය"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"මෑත යෙදුම් බලන්න"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"නිමයි"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ආපස්සට යන්න"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ආපසු යාමට, ස්පර්ශ පුවරුවවේ ඕනෑම තැනක ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න.\n\nඔබට මේ සඳහා යතුරු පුවරු කෙටිමං ක්‍රියාව + ESC ද භාවිත කළ හැක."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ඕනෑම වේලාවක ඔබේ මුල් තිරයට යාමට, ඔබේ තිරයේ පහළ සිට ඇඟිලි තුනකින් ඉහළට ස්වයිප් කරන්න."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"කදිමයි!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කළා."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"මෑත යෙදුම් බලන්න"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"ඉහළට ස්වයිප් කර ඔබේ ස්පර්ශ පුවරුව මත ඇඟිලි තුනක් භාවිතා කර සිටින්න."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"අනර්ඝ වැඩක්!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ඔබ මෑත යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ක්‍රියා යතුර"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ඔබේ යෙදුම් වෙත ප්‍රවේශ වීමට, ඔබේ යතුරු පුවරුවෙහි ක්‍රියා යතුර ඔබන්න."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"සුබ පැතුම්!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ඇඟිලි තුනක් භාවිතයෙන් ඉහළට ස්වයිප් කර අල්ලාගෙන සිටින්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"සියලුම යෙදුම් බැලීමට ඔබේ යතුරු පුවරුව භාවිත කරන්න"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ඕනෑම අවස්ථාවක ක්‍රියාකාරී යතුර ඔබන්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"තවත් අඳුරු දැන් දීප්තියේ ස්ලයිඩරයේ කොටසකි"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ඔබට දැන් දීප්ති මට්ටම තවත් අඩු කිරීමෙන් තිරය තවත් අඳුරු කළ හැක.\n\nමෙම විශේෂාංගය දැන් දීප්තියේ ස්ලයිඩරයේ කොටසක් බැවින්, අමතර අඳුරු කෙටිමං ඉවත් කරනු ලැබේ."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"තවත් අඳුරු කෙටිමං ඉවත් කරන්න"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"තවත් අඳුරු කෙටිමං ඉවත් කරන ලදි"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"සබැඳුම් හැකියාව"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ප්‍රවේශ්‍යතාව"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"උපයෝගිතා"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 166a95c..a13a1b4 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -440,7 +440,7 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuté"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Zapnuté • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuté"</string>
-    <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavenie"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastaviť"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Správa v nastaveniach"</string>
     <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Žiadne aktívne režimy}=1{{mode} je aktívny}few{# režimy sú aktívne}many{# modes are active}other{# režimov je aktívnych}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudú vás vyrušovať zvuky ani vibrácie, iba budíky, pripomenutia, udalosti a volajúci, ktorých určíte. Budete naďalej počuť všetko, čo sa rozhodnete prehrať, ako napríklad hudbu, videá a hry."</string>
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Žiadne upozornenia"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Žiadne nové upozornenia"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Stlmenie upozornení je zapnuté"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Keď dostanete priveľa upozornení naraz, hlasitosť vášho zariadenia a počet upozornení sa automaticky znížia až na dve minúty."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Keď dostanete priveľa upozornení naraz, až na dve minúty sa zníži hlasitosť  zariadenia a upozornenia sa obmedzia."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vypnúť"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odomknutím zobrazíte staršie upozornenia"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Toto zariadenie spravuje tvoj rodič"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Reproduktory a obrazovky"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Navrhované zariadenia"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Ak chcete preniesť médiá do iného zariadenia, ukončite zdieľanú reláciu"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Ukončiť"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Ako vysielanie funguje"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Pridať"</string>
     <string name="manage_users" msgid="1823875311934643849">"Spravovať použ."</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Toto upozornenie nepodporuje presun na rozdelenú obrazovku"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Aktívne miesto"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi nie je k dispozícii"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Režim priority"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Budík je nastavený"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Prispôsobiť uzamknutú obrazovku"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Uzamknutú obrazovku môžete prispôsobiť po odomknutí"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi‑Fi nie je k dispozícii"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Aktívne miesto"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera je blokovaná"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera a mikrofón sú blokované"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofón je blokovaný"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Prechádzajte pomocou klávesnice"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte sa klávesové skratky"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Prechádzajte pomocou touchpadu"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte sa gestá touchpadu, klávesové skratky a ďalšie funkcie"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto prechodu späť"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto prechodu domov"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazenie nedávnych aplikácií"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Prejsť späť"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Ak chcete prejsť späť, potiahnite kdekoľvek na touchpade troma prstami doľava alebo doprava.\n\nMôžete použiť aj klávesovú skratku, teda akčný kláves + ESC."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Na plochu môžete kedykoľvek prejsť potiahnutím troma prstami zdola obrazovky."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Výborne!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Dokončili ste gesto na prechod na plochu."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazenie nedávnych aplikácií"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Potiahnite troma prstami na touchpade nahor a pridržte ich."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Skvelé!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dokončili ste gesto na zobrazenie nedávnych aplikácií."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Akčný kláves"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ak chcete získať prístup k aplikáciám, stlačte na klávesnici akčný kláves."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Blahoželáme!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Potiahnite troma prstami nahor a pridržte ich. Viac o gestách sa dozviete klepnutím."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Zobrazte si všetky aplikácie pomocou klávesnice"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Akčný kláves môžete stlačiť kedykoľvek. Viac o gestách sa dozviete klepnutím."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Mimoriadne stmavenie je teraz súčasťou posúvača jasu"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Teraz môžete obrazovku mimoriadne stmaviť ešte ďalším znížením úrovne jasu.\n\nTáto funkcia je teraz súčasťou posúvača jasu, preto odstraňujeme skratky mimoriadneho stmavenia."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Odstrániť skratky mimoriadneho stmavenia"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Skratky mimoriadneho stmavenia boli odstránené"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Pripojenie"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Dostupnosť"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utility"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index fa80b3a..653bb58 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Zvočniki in zasloni"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Predlagane naprave"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Vhodno"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Izhodno"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Ustavi deljeno sejo za premik predstavnosti v drugo napravo."</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Ustavi"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Kako deluje oddajanje"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Dodaj"</string>
     <string name="manage_users" msgid="1823875311934643849">"Upravljaj uporabnike"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"To obvestilo ne podpira vlečenja v razdeljen zaslon."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Lokacija je aktivna"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi ni na voljo."</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prednostni način"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je nastavljen."</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Prilagajanje zaklenjenega zaslona"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Odklenite za prilagajanje zaklenjenega zaslona"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi ni na voljo."</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Lokacija je aktivna"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Fotoaparat je blokiran."</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Fotoaparat in mikrofon sta blokirana."</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon je blokiran."</string>
@@ -1396,6 +1396,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
+    <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ročica za vlečenje"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krmarjenje s tipkovnico"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Učenje bližnjičnih tipk"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krmarjenje s sledilno ploščico"</string>
@@ -1404,8 +1405,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Učenje potez na sledilni ploščici, bližnjičnih tipk in drugega"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Poteza za pomik nazaj"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Poteza za začetni zaslon"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ogled nedavnih aplikacij"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Končano"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazaj"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Za pomik nazaj povlecite levo ali desno s tremi prsti kjer koli na sledilni ploščici.\n\nUporabite lahko tudi bližnjični tipki Action + ESC."</string>
@@ -1415,14 +1415,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Za pomik na začetni zaslon lahko kadar koli s tremi prsti povlečete navzgor z dna zaslona."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Odlično!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Izvedli ste potezo za pomik na začetni zaslon."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ogled nedavnih aplikacij"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Na sledilni ploščici s tremi prsti povlecite navzgor in pridržite."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Odlično!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvedli ste potezo za ogled nedavnih aplikacij."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tipka za dejanja"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Za dostop do aplikacij pritisnite tipko za dejanja na tipkovnici."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
@@ -1446,14 +1442,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"S tremi prsti povlecite navzgor in pridržite. Dotaknite se, če želite spoznati več potez."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Uporaba tipkovnice za prikaz vseh aplikacij"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Kadar koli pritisnite tipko za dejanja. Dotaknite se, če želite spoznati več potez."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Funkcija Zelo zatemnjeno je zdaj del drsnika za svetlost"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Zdaj lahko zelo zatemnite zaslon tako, da dodatno zmanjšate raven svetlosti.\n\nKer je ta funkcija zdaj del drsnika za svetlost, bodo bližnjice do funkcije Zelo zatemnjeno odstranjene."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Odstrani bližnjice do funkcije Zelo zatemnjeno"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Bližnjice do funkcije Zelo zatemnjeno so odstranjene"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Povezljivost"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Dostopnost"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Orodja"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 0b700f8..00484a7 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altoparlantët dhe ekranet"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Pajisjet e sugjeruara"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Ndalo sesionin e ndarë për ta zhvendosur median në një pajisje tjetër"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Ndalo"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Si funksionon transmetimi"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Shto"</string>
     <string name="manage_users" msgid="1823875311934643849">"Menaxho përdoruesit"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Ky njoftim nuk mbështet zvarritjen tek ekrani i ndarë"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Vendndodhja aktive"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi nuk ofrohet"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modaliteti \"Me përparësi\""</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmi është caktuar"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Personalizo ekranin e kyçjes"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Shkyçe për të personalizuar ekranin e kyçjes"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi nuk ofrohet"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Vendndodhja aktive"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera u bllokua"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera dhe mikrofoni u bllokuan"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofoni u bllokua"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigo duke përdorur tastierën tënde"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Mëso shkurtoret e tastierës"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigo duke përdorur bllokun me prekje"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Mëso gjestet e bllokut me prekje, shkurtoret e tastierës etj."</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gjesti i kthimit prapa"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gjesti për të shkuar tek ekrani bazë"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Shiko aplikacionet e fundit"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"U krye"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kthehu prapa"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Për t\'u kthyer, rrëshqit shpejt majtas ose djathtas duke përdorur tri gishta kudo në bllokun me prekje.\n\nPër ta bërë këtë, mund të përdorësh gjithashtu shkurtoren e tastierës \"Action + ESC\"."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Për të shkuar tek ekrani bazë në çdo kohë, rrëshqit shpejt lart me tre gishta nga fundi i ekranit."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bukur!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"E ke përfunduar gjestin e kalimit tek ekrani bazë."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Shiko aplikacionet e fundit"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Rrëshqit shpejt lart dhe mbaj shtypur me tre gishta në bllokun me prekje."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Punë e shkëlqyer!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Përfundove gjestin për shikimin e aplikacioneve të fundit."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tasti i veprimit"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Për t\'u qasur në aplikacionet e tua, shtyp tastin e veprimit në tastierë."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Urime!"</string>
@@ -1446,26 +1445,15 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Rrëshqit shpejt lart dhe mbaj shtypur me tre gishta. Trokit për të mësuar më shumë gjeste."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Përdor tastierën për të shikuar të gjitha aplikacionet"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Shtyp tastin e veprimit në çdo kohë. Trokit për të mësuar më shumë gjeste."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_connectivity (4559726936546032672) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_accessibility (7969091385071475922) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_utilities (8123080090108420095) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_privacy (6577774443194551775) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_providedByApps (8346112074897919019) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_display (4749511439121053942) -->
-    <skip />
-    <!-- no translation found for qs_edit_mode_category_unknown (509314252124053550) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Modaliteti \"Shumë më i zbehtë\" tani është pjesë e rrëshqitësit të ndriçimit"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Tani mund ta bësh ekranin shumë më të zbehtë duke e ulur nivelin e ndriçimit edhe më tej.\n\nDuke qenë se kjo veçori tani është pjesë e rrëshqitësit të ndriçimit, shkurtoret e modalitetit \"Shumë më i zbehtë\" janë hequr."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Hiq shkurtoret e modalitetit \"Shumë më i zbehtë\""</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Shkurtoret e modalitetit \"Shumë më i zbehtë\" u hoqën"</string>
+    <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Lidhshmëria"</string>
+    <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Qasshmëria"</string>
+    <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Aplikacione utilitare"</string>
+    <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Privatësia"</string>
+    <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Mundësuar nga aplikacionet"</string>
+    <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekrani"</string>
+    <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nuk njihet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index b1abb59..3a940c1 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Звучници и екрани"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Предложени уређаји"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Улаз"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Излаз"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Зауставите дељену сесију да бисте преместили медијски садржај на други уређај"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Заустави"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Како функционише емитовање"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Додај"</string>
     <string name="manage_users" msgid="1823875311934643849">"Управљаj корисницима"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Ово обавештење не подржава превлачење на подељени екран"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Локација је активна"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"WiFi није доступан"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Приоритетни режим"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Аларм је подешен"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Прилагоди закључани екран"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Откључајте да бисте прилагодили закључани екран"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"WiFi није доступан"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Локација је активна"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камера је блокирана"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камера и микрофон су блокирани"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Микрофон је блокиран"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Крећите се помоћу тастатуре"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Сазнајте више о тастерским пречицама"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Крећите се помоћу тачпеда"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научите покрете за тачпед, тастерске пречице и друго"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Покрет за враћање"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Покрет за почетну страницу"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи недавно коришћене апликације"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Да бисте се вратили, превуците улево са три прста било где на тачпеду.\n\nМожете да користите и тастерску пречицу Alt + ESC за ово."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Да бисте отишли на почетни екран у било ком тренутку, превуците нагоре од дна екрана помоћу три прста."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Свака част!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Довршили сте покрет за повратак на почетну страницу."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прикажи недавно коришћене апликације"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Превуците нагоре и задржите помоћу три прста на тачпеду."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Довршили сте покрет за приказивање недавно коришћених апликација."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Тастер радњи"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Да бисте приступили апликацијама, притисните тастер радњи на тастатури."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Честитамо!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Превуците нагоре и задржите са три прста. Додирните да бисте видели више покрета."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Користите тастатуру да бисте прегледали све апликације"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Притисните тастер радњи у било ком тренутку. Додирните да бисте видели више покрета."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Додатно затамњивање је сада део клизача за осветљеност"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Сада можете додатно да затамните екран смањивањем нивоа осветљености. \n\nОва функција је сада део клизача за осветљеност, па се пречице за додатно затамњивање уклањају."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Уклони пречице за додатно затамњивање"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Уклоњене су пречице за додатно затамњивање"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Повезивање"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Приступачност"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Услужне апликације"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 8817fe8..23f476d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -576,7 +576,7 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Starta nu"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Inga aviseringar"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Det finns inga nya aviseringar"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Gradvis sänkning för aviseringar är på"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Dämpning av aviseringar är på"</string>
     <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Enheten sänker volymen och minimerar aviseringar i upp till två minuter när du får för många aviseringar samtidigt."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Inaktivera"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås upp för att se äldre aviseringar"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Högtalare och skärmar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Förslag på enheter"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stoppa din delade session för att flytta media till en annan enhet"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stoppa"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Så fungerar utsändning"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Lägg till"</string>
     <string name="manage_users" msgid="1823875311934643849">"Välj användare"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Det går inte att dra den här aviseringen till delad skärm"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Aktiv plats"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wifi är inte tillgängligt"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Prioritetsläge"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmet är aktiverat"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Anpassa låsskärmen"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Lås upp för att anpassa låsskärmen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wifi är inte tillgängligt"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Aktiv plats"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kameran är blockerad"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kameran och mikrofonen är blockerade"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofonen är blockerad"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigera med tangentbordet"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lär dig kortkommandon"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigera med styrplattan"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Lär dig rörelser för styrplattan, kortkommandon med mera"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tillbaka-rörelse"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Rörelse för att öppna startskärmen"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se de senaste apparna"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klar"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tillbaka"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Gå tillbaka genom att svepa åt vänster eller höger med tre fingrar var som helst på styrplattan.\n\nDu kan även använda kortkommandot Åtgärd + Esc."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Öppna startskärmen när som helst genom att svepa uppåt med tre fingrar från skärmens nederkant."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bra!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Du är klar med rörelsen för att öppna startskärmen."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Se de senaste apparna"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Svep uppåt med tre fingrar på styrplattan och håll kvar."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bra jobbat!"</string>
+    <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="tutorial_action_key_title" msgid="2659466586996495447">"Åtgärdstangent"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Tryck på åtgärdstangenten på tangentbordet för att komma åt dina appar."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Grattis!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Svep uppåt med tre fingrar och håll kvar. Tryck för att lära dig fler rörelser."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Använd tangentbordet för att se alla appar"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tryck på åtgärdstangenten när som helst. Tryck för att lära dig fler rörelser."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Extradimmat är nu en del av skjutreglaget för ljusstyrka"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Nu kan du göra skärmen extradimmad genom att sänka ljusstyrkan ännu mer.\n\nEftersom den här funktionen nu är en del av skjutreglaget för ljusstyrka tas kortkommandon för extradimmat bort."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Ta bort kortkommandon för extradimmat"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Kortkommandon för extradimmat har tagits bort"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Anslutning"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Tillgänglighet"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Verktyg"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 28f677a..dee6a5f 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -440,9 +440,9 @@
     <string name="zen_mode_on" msgid="9085304934016242591">"Imewashwa"</string>
     <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Imewashwa • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Imezimwa"</string>
-    <string name="zen_mode_set_up" msgid="7457957033034460064">"Weka mipangilio"</string>
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Ratibu"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Dhibiti katika mipangilio"</string>
-    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Hakuna hali za kutumika}=1{Unatumia {mode}}other{Unatumia hali #}}"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Hakuna hali zinazotumika}=1{Unatumia {mode}}other{Unatumia hali #}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele, vikumbusho, matukio na simu zinazopigwa na watu uliobainisha. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Badilisha upendavyo"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Spika na Skrini"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Vifaa Vilivyopendekezwa"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Simamisha kipindi unachoshiriki ili uhamishie maudhui kwenye kifaa kingine"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Simamisha"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Jinsi utangazaji unavyofanya kazi"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Weka"</string>
     <string name="manage_users" msgid="1823875311934643849">"Dhibiti watumiaji"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Arifa hii haitumii utaratibu wa kuburuta ili kugawa skrini"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Inatumia data ya mahali ulipo"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi-Fi haipatikani"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Hali ya kipaumbele"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Kengele imewekwa"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Wekea mapendeleo skrini iliyofungwa"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Fungua ili uweke mapendeleo ya skrini iliyofungwa"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi haipatikani"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Inatumia data ya mahali ulipo"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera imezuiwa"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera na maikrofoni zimezuiwa"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Maikrofoni imezuiwa"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Kusogeza kwa kutumia kibodi yako"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Jifunze kuhusu mikato ya kibodi"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Kusogeza kwa kutumia padi yako ya kugusa"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Jifunze kuhusu miguso ya padi ya kugusa, mikato ya kibodi na mengineyo"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Ishara ya kurudi nyuma"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Mguso wa kurudi kwenye skrini ya kwanza"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Angalia programu za hivi majuzi"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Nimemaliza"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Rudi nyuma"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Telezesha vidole vitatu kushoto au kulia mahali popote kwenye padi ya kugusa ili urudi nyuma.\n\nUnaweza pia kutumia mikato ya kibodi ya Kitendo pamoja na ESC kutekeleza kitendo hiki."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Ili uende kwenye skrini ya kwanza wakati wowote, telezesha vidole vitatu juu kutoka sehemu ya chini ya skrini yako."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Safi!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Umeweka ishara ya kwenda kwenye skrini ya kwanza."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Angalia programu za hivi majuzi"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Telezesha vidole vitatu juu kisha ushikilie kwenye padi yako ya kugusa."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kazi nzuri!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Umekamilisha mafunzo ya mguso wa kuangalia programu za hivi majuzi."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Kitufe cha vitendo"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Bonyeza kitufe cha vitendo kwenye kibodi yako ili ufikie programu zako."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Hongera!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Telezesha vidole vitatu juu na ushikilie. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Kutumia kibodi yako kuangalia programu zote"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Bonyeza kitufe cha vitendo wakati wowote. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Sasa kipunguza mwangaza zaidi ni sehemu ya kitelezi cha mwangaza"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Sasa unaweza kupunguza kiwango cha mwangaza wa skrini hata zaidi.\n\nKwa kuwa kipengele hiki sasa ni sehemu ya kitelezi cha mwangaza, njia za mkato za kipunguza mwangaza zaidi zinaondolewa."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Ondoa njia za mkato za kipunguza mwangaza zaidi"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Njia za mkato za kipunguza mwangaza zaidi zimeondolewa"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Muunganisho"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Ufikivu"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Vipengee"</string>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 3efe7a5..2a27b47 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -26,10 +26,6 @@
     <dimen name="keyguard_clock_top_margin">8dp</dimen>
     <dimen name="keyguard_smartspace_top_offset">0dp</dimen>
 
-    <!-- New keyboard shortcut helper -->
-    <dimen name="shortcut_helper_width">864dp</dimen>
-    <dimen name="shortcut_helper_height">728dp</dimen>
-
     <!-- QS-->
     <dimen name="qs_panel_padding_top">16dp</dimen>
     <dimen name="qs_panel_padding">24dp</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 3b8e3c2..92da908 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ஸ்பீக்கர்கள் &amp; டிஸ்ப்ளேக்கள்"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"பரிந்துரைக்கப்படும் சாதனங்கள்"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"மீடியாவை வேறொரு சாதனத்திற்கு மாற்ற \'பகிரப்படும் அமர்வை\' நிறுத்தவும்"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"நிறுத்து"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"பிராட்காஸ்ட் எவ்வாறு செயல்படுகிறது?"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"சேர்"</string>
     <string name="manage_users" msgid="1823875311934643849">"பயனர்களை நிர்வகித்தல்"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"பிரிக்கப்பட்ட திரைக்குள் இந்த அறிவிப்பை இழுத்துவிட முடியாது"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"இருப்பிடம் இயக்கத்திலுள்ளது"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"வைஃபை கிடைக்கவில்லை"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"முன்னுரிமைப் பயன்முறை"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"அலாரம் அமைக்கப்பட்டுள்ளது"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"பூட்டுத் திரையை பிரத்தியேகமாக்கு"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"பூட்டுத் திரையைப் பிரத்தியேகப்படுத்த அன்லாக் செய்யுங்கள்"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"வைஃபை கிடைக்கவில்லை"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"இருப்பிடம் இயக்கத்திலுள்ளது"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"கேமரா தடுக்கப்பட்டுள்ளது"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"கேமராவும் மைக்ரோஃபோனும் தடுக்கப்பட்டுள்ளன"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"மைக்ரோஃபோன் தடுக்கப்பட்டுள்ளது"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"கீபோர்டு ஷார்ட்கட்கள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"டச்பேடைப் பயன்படுத்திச் செல்லுதல்"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"டச்பேட் சைகைகள், கீபோர்டு ஷார்ட்கட்கள் மற்றும் பலவற்றைத் தெரிந்துகொள்ளுங்கள்"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"பின்செல்வதற்கான சைகை"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"முகப்பிற்குச் செல்வதற்கான சைகை"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"முடிந்தது"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"பின்செல்"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"பின்செல்ல, உங்கள் டச்பேடில் எங்கு வேண்டுமானாலும் இடது அல்லது வலதுபுறமாக மூன்று விரல்களால் ஸ்வைப் செய்யவும்.\n\nஇதற்கு நீங்கள் கீபோர்டு ஷார்ட்கட் செயல்பாடுகள் + Esc பட்டனையும் பயன்படுத்தலாம்."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"எப்போது வேண்டுமானாலும் உங்கள் முகப்புத் திரைக்குச் செல்ல, மூன்று விரல்களால் திரையின் கீழிருந்து மேல்நோக்கி ஸ்வைப் செய்யவும்."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"அற்புதம்!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"முகப்புக்குச் செல்வதற்கான சைகையை நிறைவுசெய்துவிட்டீர்கள்."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"சமீபத்திய ஆப்ஸைக் காட்டுதல்"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"உங்கள் டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும்."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"அருமை!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"சமீபத்தில் பயன்படுத்திய ஆப்ஸுக்கான சைகை பயிற்சியை நிறைவுசெய்துவிட்டீர்கள்."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ஆக்‌ஷன் பட்டன்"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ஆப்ஸை அணுக உங்கள் கீபோர்டில் உள்ள ஆக்‌ஷன் பட்டனை அழுத்துங்கள்."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"வாழ்த்துகள்!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"அனைத்து ஆப்ஸையும் பார்க்க உங்கள் கீபோர்டைப் பயன்படுத்துங்கள்"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"எப்போது வேண்டுமானாலும் ஆக்ஷன் பட்டனை அழுத்தலாம். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"மிகக் குறைவான வெளிச்சம் அம்சம் இப்போது ஒளிர்வு ஸ்லைடரின் ஒரு பகுதியாகும்"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"இப்போது ஒளிர்வு அளவைக் குறைப்பதன் மூலம் திரையை மிகக் குறைவான வெளிச்சத்தில் பயன்படுத்தலாம்.\n\nஇந்த அம்சம் ஒளிர்வு ஸ்லைடர் அம்சத்தின் ஒரு பகுதி என்பதால் மிகக் குறைவான வெளிச்சத்திற்கான ஷார்ட்கட்கள் அகற்றப்படுகின்றன."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"மிகக் குறைவான வெளிச்சத்திற்கான ஷார்ட்கட்களை அகற்றும்"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"மிகக் குறைவான வெளிச்சத்திற்கான ஷார்ட்கட்கள் அகற்றப்பட்டன"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"இணைப்புநிலை"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"மாற்றுத்திறன் வசதி"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"யூட்டிலிட்டி சேவைகள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 84fc147..16500ed 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -577,7 +577,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"నోటిఫికేషన్‌లు లేవు"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"కొత్త నోటిఫికేషన్‌లు ఏవీ లేవు"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"నోటిఫికేషన్ కూల్‌డౌన్ ఆన్ అయింది"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ఒకేసారి పలు నోటిఫికేషన్‌లు వస్తే, పరికర వాల్యూమ్, అలర్ట్‌లు ఆటోమేటిక్‌గా 2 నిమిషాలకు తగ్గించబడతాయి."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ఒకేసారి పలు నోటిఫికేషన్లు వస్తే, పరికర వాల్యూమ్, అలర్ట్స్ ఆటోమేటిగ్గా 2 నిమిషాలకు తగ్గించబడతాయి."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ఆఫ్ చేయండి"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"పాత నోటిఫికేషన్‌ల కోసం అన్‌లాక్ చేయండి"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు"</string>
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"స్పీకర్‌లు &amp; డిస్‌ప్లేలు"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"సూచించబడిన పరికరాలు"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ఇన్‌పుట్"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"అవుట్‌పుట్"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"మీడియాను మరొక పరికరానికి తరలించడానికి మీ షేర్ చేసిన సెషన్‌ను ఆపండి"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"ఆపండి"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"ప్రసారం కావడం అనేది ఎలా పని చేస్తుంది"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"జోడించండి"</string>
     <string name="manage_users" msgid="1823875311934643849">"యూజర్‌లను మేనేజ్ చేయండి"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"ఈ నోటిఫికేషన్ స్ప్లిట్ స్క్రీన్‌కు లాగడాన్ని సపోర్ట్ చేయదు"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"లొకేషన్ యాక్టివ్‌గా ఉంది"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi అందుబాటులో లేదు"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ముఖ్యమైన ఫైల్స్ మోడ్"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"అలారం సెట్ చేశాను"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"లాక్ స్క్రీన్ అనుకూలంగా మార్చండి"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"లాక్ స్క్రీన్‌ను అనుకూలంగా మార్చుకోవడానికి అన్‌లాక్ చేయండి"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi అందుబాటులో లేదు"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"లొకేషన్ యాక్టివ్‌గా ఉంది"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"కెమెరా బ్లాక్ చేయబడింది"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"కెమెరా, మైక్రోఫోన్ బ్లాక్ చేయబడ్డాయి"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"మైక్రోఫోన్ బ్లాక్ చేయబడింది"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"మీ కీబోర్డ్ ఉపయోగించి నావిగేట్ చేయండి"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"కీబోర్డ్ షార్ట్‌కట్‌ల గురించి తెలుసుకోండి"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"మీ టచ్‌ప్యాడ్‌ని ఉపయోగించి నావిగేట్ చేయండి"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"టచ్‌ప్యాడ్ సంజ్ఞలు, కీబోర్డ్ షార్ట్‌కట్‌లు, అలాగే మరిన్నింటిని గురించి తెలుసుకోండి"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"వెనుకకు పంపే సంజ్ఞ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"హోమ్‌కు పంపే సంజ్ఞ"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ఇటీవలి యాప్‌లను చూడండి"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"పూర్తయింది"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"వెనుకకు"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"వెనుకకు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో ఎక్కడైనా మూడు వేళ్లను ఉపయోగించి ఎడమ లేదా కుడి వైపునకు స్వైప్ చేయండి.\n\nమీరు దీని కోసం + ESC కీబోర్డ్ షార్ట్‌కట్ యాక్షన్‌ను కూడా ఉపయోగించవచ్చు."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ఏ సమయంలోనైనా మీ మొదటి స్క్రీన్‌కు వెళ్లడానికి, మీ స్క్రీన్ కింది నుండి మూడు వేళ్లతో పైకి స్వైప్ చేయండి."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"సూపర్!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"మొదటి స్క్రీన్‌కు వెళ్ళడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ఇటీవలి యాప్‌లను చూడండి"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"మీ టచ్‌ప్యాడ్‌లో మూడు వేళ్లను ఉపయోగించి పైకి స్వైప్ చేసి, హోల్డ్ చేయండి."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"పనితీరు అద్భుతంగా ఉంది!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"మీరు ఇటీవలి యాప్‌ల వీక్షణ సంజ్ఞను పూర్తి చేశారు."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"యాక్షన్ కీ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"మీ యాప్‌లను యాక్సెస్ చేయడానికి, మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"అభినందనలు!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"మూడు వేళ్లతో పైకి స్వైప్ చేసి, హోల్డ్ చేయండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"యాప్‌లన్నింటినీ చూడటానికి మీ కీబోర్డ్‌ను ఉపయోగించండి"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ఏ సమయంలోనైనా యాక్షన్ కీని నొక్కండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"కాంతిని మరింత డిమ్ చేసే ఫీచర్ ఇప్పుడు బ్రైట్‌నెస్ స్లయిడర్‌లో ఒక భాగం"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"మీరు ఇప్పుడు బ్రైట్‌నెస్ స్థాయిని మరింత తగ్గించడం ద్వారా స్క్రీన్‌ను కాంతిని మరింత డిమ్ చేయవచ్చు.\n\nఈ ఫీచర్ ఇప్పుడు బ్రైట్‌నెస్ స్లయిడర్‌లో భాగం కాబట్టి, కాంతిని మరింత డిమ్ చేసే షార్ట్‌కట్‌లు తీసివేయబడుతున్నాయి."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"కాంతిని మరింత డిమ్ చేసే షార్ట్‌కట్‌లను తీసివేయండి"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"కాంతిని మరింత డిమ్ చేసే షార్ట్‌కట్‌లు తీసివేయబడ్డాయి"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"కనెక్టివిటీ"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibility"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"యుటిలిటీలు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 1d7a782..6bfc9f9 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ลำโพงและจอแสดงผล"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"อุปกรณ์ที่แนะนำ"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"อินพุต"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"เอาต์พุต"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"หยุดเซสชันที่แชร์อยู่เพื่อย้ายสื่อไปยังอุปกรณ์อื่น"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"หยุด"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"วิธีการทำงานของการออกอากาศ"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"เพิ่ม"</string>
     <string name="manage_users" msgid="1823875311934643849">"จัดการผู้ใช้"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"การแจ้งเตือนนี้ไม่รองรับการลากเพื่อแยกหน้าจอ"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"ตำแหน่งที่ใช้งานอยู่"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"ใช้ Wi‑Fi ไม่ได้"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"โหมดลำดับความสำคัญ"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ตั้งปลุกแล้ว"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"ปรับแต่งหน้าจอล็อก"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"ปลดล็อกเพื่อปรับแต่งหน้าจอล็อก"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi ไม่พร้อมใช้งาน"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"ตำแหน่งที่ใช้งานอยู่"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"กล้องถูกบล็อกอยู่"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"กล้องและไมโครโฟนถูกบล็อกอยู่"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"ไมโครโฟนถูกบล็อกอยู่"</string>
@@ -1396,6 +1396,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
+    <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ดูข้อมูลเกี่ยวกับแป้นพิมพ์ลัด"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ไปยังส่วนต่างๆ โดยใช้ทัชแพด"</string>
@@ -1404,8 +1405,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด แป้นพิมพ์ลัด และอื่นๆ"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ท่าทางสัมผัสสำหรับย้อนกลับ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ท่าทางสัมผัสสำหรับหน้าแรก"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ดูแอปล่าสุด"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"เสร็จสิ้น"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"หากต้องการย้อนกลับ ให้ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาที่ใดก็ได้บนทัชแพด\n\nหรือใช้การดำเนินการแป้นพิมพ์ลัด + ESC ได้เช่นเดียวกัน"</string>
@@ -1415,14 +1415,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ใช้ 3 นิ้วปัดขึ้นจากด้านล่างของหน้าจอเพื่อไปที่หน้าจอหลักได้ทุกเมื่อ"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ดีมาก"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ดูแอปล่าสุด"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"เยี่ยมมาก"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"คุณทำท่าทางสัมผัสเพื่อดูแอปล่าสุดสำเร็จแล้ว"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ปุ่มดำเนินการ"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"หากต้องการเข้าถึงแอป ให้กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ยินดีด้วย"</string>
@@ -1446,14 +1442,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ใช้แป้นพิมพ์เพื่อดูแอปทั้งหมด"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"กดปุ่มดำเนินการได้ทุกเมื่อ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"ตอนนี้การหรี่แสงเพิ่มเติมเป็นส่วนหนึ่งของแถบเลื่อนความสว่างแล้ว"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"ตอนนี้คุณสามารถหรี่แสงหน้าจอเพิ่มเติมได้โดยการลดระดับความสว่างลงไปอีก\n\nเนื่องจากฟีเจอร์นี้เป็นส่วนหนึ่งของแถบเลื่อนความสว่างแล้ว เราจึงนำทางลัดหรี่แสงเพิ่มเติมออก"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"นำทางลัดหรี่แสงเพิ่มเติมออก"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"นำทางลัดหรี่แสงเพิ่มเติมออกแล้ว"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"การเชื่อมต่อ"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"การช่วยเหลือพิเศษ"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ยูทิลิตี"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 00be27b..4f3819b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Mga Speaker at Display"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Mga Iminumungkahing Device"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"Input"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"Output"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Ihinto ang iyong nakabahaging session para maglipat ng media sa ibang device"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Ihinto"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Paano gumagana ang pag-broadcast"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"Magdagdag"</string>
     <string name="manage_users" msgid="1823875311934643849">"Pamahalaan ang mga user"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Hindi sinusuportahan ng notification na ito ang pag-drag sa split screen"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Aktibo ang lokasyon"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Hindi available ang Wi‑Fi"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Priority mode"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Nakatakda ang alarm"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"I-customize ang lock screen"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"I-unlock para i-customize ang lock screen"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Hindi available ang Wi-Fi"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Aktibo ang lokasyon"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Naka-block ang camera"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Naka-block ang camera at mikropono"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Naka-block ang mikropono"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Mag-navigate gamit ang iyong keyboard"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Matuto ng mga keyboard shortcut"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Mag-navigate gamit ang iyong touchpad"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Matuto ng mga galaw sa touchpad, keyboard shortcut, at higit pa"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Galaw para bumalik"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Galaw para sa Home"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Tingnan ang mga kamakailang app"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tapos na"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Bumalik"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para bumalik, mag-swipe pakaliwa o pakanan gamit ang tatlong daliri saanman sa touchpad.\n\nPuwede mo ring gamitin ang keyboard shortcut na Action + ESC para dito."</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para pumunta sa iyong home screen anumang oras, mag-swipe pataas gamit ang tatlong daliri mula sa ibaba ng screen. mo."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Magaling!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Nakumpleto mo na ang galaw para pumunta sa home."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Tingnan ang mga kamakailang app"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Mag-swipe pataas at i-hold gamit ang tatlong daliri sa iyong touchpad."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Magaling!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Nakumpleto mo ang galaw sa pag-view ng mga kamakailang app."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para ma-access ang iyong mga app, pindutin ang action key sa keyboard mo."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Binabati kita!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Mag-swipe pataas at i-hold gamit ang tatlong daliri. I-tap para matuto pa tungkol sa mga galaw."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gamitin ang iyong keyboard para tingnan ang lahat ng app"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pindutin ang action key kahit kailan. I-tap para matuto pa tungkol sa mga galaw."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Bahagi na ng slider ng liwanag ang extra dim"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Puwede mo nang gawing extra dim ang screen sa pamamagitan ng pagbababa pa sa antas ng liwanag .\n\nDahil bahagi na ang feature na ito ng slider ng liwanag, aalisin na ang mga shortcut sa extra dim."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Alisin ang mga shortcut sa extra dim"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Inalis ang mga shortcut sa extra dim"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Pagkakonekta"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibility"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Mga Utility"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 0031687..19fad73 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Hoparlörler ve Ekranlar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Önerilen Cihazlar"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Medyayı başka bir cihaza taşımak için paylaşılan oturumunuzu durdurun"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Durdur"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Yayınlamanın işleyiş şekli"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Ekle"</string>
     <string name="manage_users" msgid="1823875311934643849">"Kullanıcıları yönet"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Bu bildirim bölünmüş ekrana sürüklemeyi desteklemiyor"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Konum etkin"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Kablosuz kullanılamıyor"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Öncelik modu"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm kuruldu"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Kilit ekranını özelleştir"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Kilit ekranını özelleştirmek için kilidi açın"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Kablosuz bağlantı kullanılamıyor"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Konum etkin"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera engellendi"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera ve mikrofon engellendi"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon engellendi"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klavyenizi kullanarak gezinin"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klavye kısayollarını öğrenin"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Dokunmatik alanınızı kullanarak gezinin"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Dokunmatik alan hareketlerini, klavye kısayollarını ve daha fazlasını öğrenin"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geri hareketi"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Ana sayfa hareketi"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son uygulamaları görüntüle"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Bitti"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri dön"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Geri dönmek için dokunmatik alanın herhangi bir yerinde üç parmağınızla sola veya sağa kaydırın.\n\nDilerseniz işlem düğmesi + Esc klavye kısayolunu kullanarak da geri dönebilirsiniz."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"İstediğiniz zaman ana ekrana gitmek için üç parmağınızla ekranınızın altından yukarı doğru kaydırın."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Güzel!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ana ekrana git hareketini tamamladınız."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Son uygulamaları görüntüle"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Dokunmatik alanda üç parmağınızla yukarı doğru kaydırıp basılı tutun."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tebrikler!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son uygulamaları görüntüleme hareketini tamamladınız."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Eylem tuşu"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Uygulamalarınıza erişmek için klavyenizdeki eylem tuşuna basın."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tebrikler!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Üç parmağınızla yukarı kaydırıp basılı tutun. Daha fazla hareket öğrenmek için dokunun."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Tüm uygulamaları görüntülemek için klavyenizi kullanın"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"İstediğiniz zaman eylem tuşuna basın. Daha fazla hareket öğrenmek için dokunun."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Ekstra loş özelliği, parlaklık kaydırma çubuğuna eklendi"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Artık parlaklık seviyesini daha da düşürerek ekranı ekstra loş hale getirebilirsiniz.\n\nBu özellik artık parlaklık kaydırma çubuğuna eklendiği için ekstra loş kısayolları kaldırılıyor."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Ekstra loş kısayollarını kaldır"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Ekstra loş kısayolları kaldırıldı"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Bağlantı"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Erişilebilirlik"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Yardımcı programlar"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 715b179..453ce4a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Колонки й екрани"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Пропоновані пристрої"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Зупиніть сеанс спільного доступу, щоб перенести медіаконтент на інший пристрій"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Зупинити"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Як працює трансляція"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Додати"</string>
     <string name="manage_users" msgid="1823875311934643849">"Керувати користувачами"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Це сповіщення не підтримує режим розділеного екрана"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Геолокацію ввімкнено"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Мережа Wi-Fi недоступна"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Режим пріоритету"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будильник установлено"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Налаштувати заблокований екран"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Розблокуйте, щоб налаштувати заблокований екран"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Мережа Wi-Fi недоступна"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Геолокацію ввімкнено"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Камеру заблоковано"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Камеру й мікрофон заблоковано"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Мікрофон заблоковано"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігація за допомогою клавіатури"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Комбінації клавіш: докладніше"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігація за допомогою сенсорної панелі"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Жести для сенсорної панелі, комбінації клавіш тощо: докладніше"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест \"Назад\""</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест переходу на головний екран"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Переглянути нещодавні додатки"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Щоб перейти назад, проведіть трьома пальцями вліво або вправо по сенсорній панелі.\n\nТакож можна скористатися комбінацією \"клавіша дії\" + ESC."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Щоб будь-коли перейти на головний екран, проведіть трьома пальцями вгору від нижнього краю екрана."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Чудово!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ви виконали жест переходу на головний екран."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Переглянути нещодавні додатки"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Проведіть трьома пальцями вгору й утримуйте їх на сенсорній панелі."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Чудово!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ви виконали жест для перегляду нещодавно відкритих додатків."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавіша дії"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Щоб переглянути додатки, натисніть клавішу дії на клавіатурі."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Вітаємо!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Проведіть трьома пальцями вгору й утримуйте їх на екрані. Натисніть, щоб дізнатися про інші жести."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Щоб переглянути всі додатки, використовуйте клавіатуру"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Будь-коли натисніть клавішу дії. Натисніть, щоб дізнатися про інші жести."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Тепер функція додаткового зменшення яскравості активується за допомогою повзунка"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Тепер ви можете зробити екран ще темнішим, додатково зменшуючи рівень яскравості.\n\nОскільки тепер ця функція активується за допомогою повзунка яскравості, комбінації клавіш для додаткового зменшення яскравості буде вилучено."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Вилучити комбінації клавіш для додаткового зменшення яскравості"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Комбінації клавіш для додаткового зменшення яскравості вилучено"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Обмін даними"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Функції доступності"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Утиліти"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 78a7f428..9865c8d 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1179,6 +1179,8 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"%%<xliff:g id="PERCENTAGE">%1$d</xliff:g>"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"اسپیکرز اور ڈسپلیز"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"تجویز کردہ آلات"</string>
+    <string name="media_input_group_title" msgid="2057057473860783021">"ان پٹ"</string>
+    <string name="media_output_group_title" msgid="6789001895863332576">"آؤٹ پٹ"</string>
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"میڈیا کو دوسرے آلے پر منتقل کرنے کے لیے اپنا مشترکہ سیشن بند کریں"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"بند کریں"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"براڈکاسٹنگ کیسے کام کرتا ہے"</string>
@@ -1289,8 +1291,7 @@
     <string name="add" msgid="81036585205287996">"شامل کریں"</string>
     <string name="manage_users" msgid="1823875311934643849">"صارفین کا نظم کریں"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"یہ اطلاع اسپلٹ اسکرین پر گھسیٹنے کو سپورٹ نہیں کرتی ہے"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"مقام فعال ہے"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"‏Wi-Fi دستیاب نہیں ہے"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"ترجیحی وضع"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"الارم سیٹ ہوگیا"</string>
@@ -1347,8 +1348,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"مقفل اسکرین کو حسب ضرورت بنائیں"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"مقفل اسکرین کو حسب ضرورت بنانے کے لیے غیر مقفل کریں"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"‏Wi-Fi دستیاب نہیں ہے"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"مقام فعال ہے"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"کیمرا مسدود ہے"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"کیمرا اور مائیکروفون مسدود ہے"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"مائیکروفون مسدود ہے"</string>
@@ -1396,6 +1396,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"اپنے کی بورڈ کا استعمال کر کے نیویگیٹ کریں"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"کی بورڈ شارٹ کٹس جانیں"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"اپنے ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string>
@@ -1404,8 +1406,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ٹچ پیڈ کے اشارے، کی بورڈ شارٹ کٹس اور مزید جانیں"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"پیچھے جانے کا اشارہ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ہوم کا اشارہ"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"حالیہ ایپس دیکھیں"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ہو گیا"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"‏واپس جانے کے لیے، ٹچ پیڈ پر کہیں بھی تین انگلیوں کی مدد سے دائیں یا بائیں سوائپ کریں۔\n\nآپ اس کیلئے کی بورڈ شارٹ کٹ ایکشن + Esc کا بھی استعمال کر سکتے ہیں۔"</string>
@@ -1415,14 +1416,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"کسی بھی وقت اپنی ہوم اسکرین پر جانے کے لیے، تین انگلیوں کی مدد سے اپنی اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں۔"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"عمدہ!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا۔"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"حالیہ ایپس دیکھیں"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"اپنے ٹچ پیڈ پر تین انگلیوں کا استعمال کرتے ہوئے اوپر کی طرف سوائپ کریں اور دبائے رکھیں۔"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"بہترین!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس کا اشارہ مکمل کر لیا ہے۔"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"ایکشن کلید"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"اپنی ایپس تک رسائی حاصل کرنے کے لیے، اپنے کی بورڈ پر ایکشن کلید کو دبائیں۔"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"مبارکباد!"</string>
@@ -1446,14 +1443,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"تین انگلیوں سے اوپر کی طرف سوائپ کریں اور دبائے رکھیں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"سبھی ایپس دیکھنے کے لیے اپنے کی بورڈ کا استعمال کریں"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"کسی بھی وقت ایکشن کلید دبائیں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"اضافی دھندلا اب چمک سلائیڈر کا حصہ ہے"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"آپ چمکیلے پن لیول کو مزید کم کر کے اپنی اسکرین کو اضافی دھندلی بنا سکتے ہیں۔\n\nچونکہ یہ خصوصیت اب چمکیلے پن کے سلائیڈر کا حصہ ہے، اس لیے اضافی دھندلا شارٹ کٹس کو ہٹایا جا رہا ہے۔"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"اضافی دھندلا شارٹ کٹس کو ہٹائیں"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"اضافی دھندلا شارٹ کٹس ہٹا دیے گئے"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"کنیکٹویٹی"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"ایکسیسبیلٹی"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"یوٹیلیٹیز"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 51cf8b5..cd4cc0d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -442,7 +442,7 @@
     <string name="zen_mode_off" msgid="1736604456618147306">"Yoqilmagan"</string>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"Sozlash"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Sozlamalarda boshqarish"</string>
-    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Hech qanday rejim faol emas}=1{{mode} faol}other{# ta rejim faol}}"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{0 ta rejim faol}=1{{mode} faol}other{# ta rejim faol}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan chaqiruvlar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Sozlash"</string>
@@ -576,7 +576,7 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Yangi bildirishoma yoʻq"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildirishnomalarni sekinlatish yoniq"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Bildirishnomalar ovozini pasaytirish yoniq"</string>
     <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Bir vaqtda juda koʻp bildirishnoma olsangiz, qurilmangiz tovushi va ogohlantirishlar 2 daqiqagacha avtomatik pasaytiriladi."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Faolsizlantirish"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eskilarini koʻrish uchun qulfni yeching"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Karnaylar va displeylar"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Taklif qilingan qurilmalar"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Mediani boshqa qurilmaga koʻchirish uchun umumiy seansingizni toʻxtating"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Toʻxtatish"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Translatsiya qanday ishlaydi"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Kiritish"</string>
     <string name="manage_users" msgid="1823875311934643849">"Foyd-ni boshqarish"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Bu bildirishnoma ikkiga ajratilgan ekranda ishlamaydi."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Joylashuv faol"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi ishlamayapti"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Imtiyozli rejim"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signal oʻrnatildi"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Ekran qulfini moslash"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Ekran qulfini sozlash uchun qulfni oching"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Wi-Fi mavjud emas"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Joylashuv faol"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Kamera bloklangan"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Kamera va mikrofon bloklangan"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Mikrofon bloklangan"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviatura yordamida kezing"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tezkor tugmalar haqida"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Sensorli panel yordamida kezing"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sensorli panel ishoralari, tezkor tugmalar va boshqalar haqida"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Orqaga qaytish ishorasi"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Asosiy ekran ishorasi"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Oxirgi ilovalarni koʻrish"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Ortga qaytish uchun sensorli panelda uchta barmoqni chapga yoki oʻngga suring.\n\nBuning uchun Action + ESC tezkor tugmalaridan ham foydalanishingiz mumkin."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Istalgan vaqtda bosh ekranga oʻtish uchun ekranning pastidan uchta barmoq bilan tepaga suring."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Yaxshi!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Bosh ekranni ochish ishorasi darsini tamomladingiz."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Oxirgi ilovalarni koʻrish"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Sensorli panelda uchta barmoq bilan tepaga surib, bosib turing."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Barakalla!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Oxirgi ilovalarni koʻrish ishorasini tugalladingiz."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Amal tugmasi"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ilovalarga kirish uchun klaviaturadagi amal tugmasini bosing"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tabriklaymiz!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Uchta barmoq bilan tepaga surib, bosib turing. Boshqa ishoralar bilan tanishish uchun bosing."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Klaviatura orqali barcha ilovalarni koʻrish"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Amal tugmasini istalganda bosing. Boshqa ishoralar bilan tanishish uchun bosing."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Juda xira endi yorqinlik slayderida joylashgan"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Endi yorqinlik darajasini yanada pasaytirish orqali ekranni yanada xiralashtirishingiz mumkin.\n\nBu funksiya yorqinlik slayderiga kiritilgani uchun \"juda xira\" buyruqlari olib tashlanmoqda."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Juda xira buyruqlarini olib tashlash"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Juda xira buyruqlari olib tashlandi"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Aloqa"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Qulayliklar"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Vositalar"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index fb9abd7..28a6900 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Loa và màn hình"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Thiết bị được đề xuất"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Dừng phiên chia sẻ của bạn để chuyển nội dung nghe nhìn sang thiết bị khác"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Dừng"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cách tính năng truyền hoạt động"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Thêm"</string>
     <string name="manage_users" msgid="1823875311934643849">"Quản lý ng.dùng"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Thông báo này không hỗ trợ thao tác kéo để chia đôi màn hình"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Vị trí đang hoạt động"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Không có Wi‑Fi"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Chế độ ưu tiên"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Đã đặt chuông báo"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Tuỳ chỉnh màn hình khoá"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Mở khoá để tuỳ chỉnh màn hình khoá"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"Không có Wi-Fi"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Vị trí đang hoạt động"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Máy ảnh bị chặn"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Máy ảnh và micrô bị chặn"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Micrô bị chặn"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Di chuyển bằng bàn phím"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tìm hiểu về phím tắt"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Di chuyển bằng bàn di chuột"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Tìm hiểu về cử chỉ trên bàn di chuột, phím tắt và nhiều mục khác"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Cử chỉ quay lại"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Cử chỉ chuyển đến màn hình chính"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Xem các ứng dụng gần đây"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Xong"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Quay lại"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Để quay lại, hãy dùng 3 ngón tay vuốt sang trái hoặc sang phải ở vị trí bất kỳ trên bàn di chuột.\n\nBạn cũng có thể dùng phím tắt Hành động + ESC cho thao tác này."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Để chuyển đến màn hình chính bất cứ lúc nào, hãy dùng 3 ngón tay vuốt lên từ cuối màn hình lên."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Tốt lắm!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Bạn đã thực hiện xong cử chỉ chuyển đến màn hình chính."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Xem các ứng dụng gần đây"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Dùng 3 ngón tay vuốt lên và giữ trên bàn di chuột."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tuyệt vời!"</string>
+    <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="tutorial_action_key_title" msgid="2659466586996495447">"Phím hành động"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Để truy cập vào các ứng dụng của bạn, hãy nhấn phím hành động trên bàn phím."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Xin chúc mừng!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Dùng 3 ngón tay vuốt lên và giữ. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Sử dụng bàn phím để xem tất cả ứng dụng"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Nhấn phím hành động bất cứ lúc nào. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Tính năng siêu tối hiện đã có trên thanh trượt điều chỉnh độ sáng"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Giờ đây, bạn có thể giảm thêm độ sáng để làm màn hình trở nên siêu tối.\n\nVì tính năng này hiện đã có trên thanh trượt điều chỉnh độ sáng nên lối tắt đến tính năng siêu tối sẽ bị xoá."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Xoá lối tắt đến tính năng siêu tối"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Đã xoá lối tắt đến tính năng siêu tối"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Khả năng kết nối"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Hỗ trợ tiếp cận"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Phần mềm tiện ích"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d311aa3..267f392 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -442,7 +442,7 @@
     <string name="zen_mode_off" msgid="1736604456618147306">"已关闭"</string>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"设置"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在设置中管理"</string>
-    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{未启用任何模式}=1{{mode}已启用}other{# 个模式已启用}}"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{未启用任何模式}=1{已启用“{mode}”模式}other{已启用 # 个模式}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"您将不会受到声音和振动的打扰(闹钟、提醒、活动和所指定来电者的相关提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"您将不会受到声音和振动的打扰(闹钟提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"自定义"</string>
@@ -576,8 +576,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"没有通知"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"没有新通知"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"“通知音量渐降”设置已开启"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"当您一次收到过多通知时,设备音量会自动降低,提醒次数也会自动减少,这种状况最长可持续 2 分钟。"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"已触发“通知音量渐降”"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"如果您在短时间内收到很多通知,设备音量和提醒次数会自动降低,最长持续 2 分钟。"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"关闭"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解锁即可查看旧通知"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"此设备由您的家长管理"</string>
@@ -667,7 +667,7 @@
     <string name="volume_ringer_status_normal" msgid="1339039682222461143">"响铃"</string>
     <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"振动"</string>
     <string name="volume_ringer_status_silent" msgid="3691324657849880883">"静音"</string>
-    <string name="media_device_cast" msgid="4786241789687569892">"投屏"</string>
+    <string name="media_device_cast" msgid="4786241789687569892">"投放"</string>
     <string name="stream_notification_unavailable" msgid="4313854556205836435">"该功能无法使用,因为铃声被静音"</string>
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"“勿扰”模式已开启,因此无法调整音量"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"“勿扰”模式已开启,因此无法调整音量"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"音箱和显示屏"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建议的设备"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"停止共享的会话,即可将媒体移到其他设备"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"停止"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"广播的运作方式"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"添加"</string>
     <string name="manage_users" msgid="1823875311934643849">"管理用户"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"此通知不支持拖动到分屏中"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"正在使用位置信息"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"WLAN 已关闭"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"优先模式"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"闹钟已设置"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"自定义锁屏"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"解锁以自定义锁定屏幕"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"没有 WLAN 连接"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"正在使用位置信息"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"已禁用摄像头"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"已禁用摄像头和麦克风"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"已禁用麦克风"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用键盘导航"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"了解键盘快捷键"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用触控板导航"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"了解触控板手势、键盘快捷键等"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返回手势"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主屏幕手势"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近用过的应用"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"如要返回,请使用三根手指在触控板上的任意位置左滑或右滑。\n\n您也可以使用键盘快捷操作键 + ESC 键进行返回。"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"若要随时进入主屏幕,请用三根手指从屏幕的底部向上滑动。"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"很好!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"您完成了“前往主屏幕”手势教程。"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近用过的应用"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"在触控板上用三根手指向上滑动并按住。"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"您已完成“查看最近用过的应用”的手势教程。"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"快捷操作按键"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"如要访问您的应用,请按下键盘上的快捷操作按键。"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"恭喜!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三根手指向上滑动并按住。点按即可了解更多手势。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用键盘查看所有应用"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"您可随时按下快捷操作按键。点按即可了解更多手势。"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"“极暗”功能现已在亮度滑块中"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"现在,您可进一步调低亮度,将屏幕调成极暗。\n\n由于此功能现已在亮度滑块中,“极暗”快捷方式即将被移除。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"移除“极暗”快捷方式"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"“极暗”快捷方式已移除"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"连接"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"无障碍功能"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"实用程序"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 91ae846..c08835a 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -576,8 +576,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"沒有通知"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"沒有新通知"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"調低通知強度功能已開啟"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"如果同一時間收到太多通知,裝置會在最長 2 分鐘內調低音量,並減少警示。"</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"通知緩和已開啟"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"當你在短時間內收到太多通知時,裝置就會調低音量並減少通知數量最多兩分鐘。"</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"關閉"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解鎖即可查看舊通知"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"此裝置由你的家長管理"</string>
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"停止共享工作階段以移動媒體至其他裝置"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"停止"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播運作方式"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"新增"</string>
     <string name="manage_users" msgid="1823875311934643849">"管理使用者"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"此通知無法拖曳到分割螢幕中。"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"正在使用位置權限"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi 已關閉"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"優先模式"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"已設定鬧鐘"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"自訂上鎖畫面"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"解鎖後即可自訂上鎖畫面"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"無法連線至 Wi-Fi"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"正在使用位置權限"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"已封鎖相機"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"已封鎖相機和麥克風"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"已封鎖麥克風"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤導覽"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"瞭解鍵盤快速鍵"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板導覽"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"瞭解觸控板手勢、鍵盤快速鍵等等"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返去手勢"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主畫面手勢"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"用三隻手指在觸控板上任何一處向左或向右滑動即可返回。\n\n你也可使用鍵盤快速鍵 Action 鍵 + Esc 鍵執行此操作。"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"只要用三隻手指從螢幕底部向上滑動,隨時可以返回主畫面。"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"做得好!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"你已完成「返回主畫面」手勢的教學課程。"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近使用的應用程式"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"用三隻手指在觸控板向上滑動並按住。"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"做得好!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢的教學課程。"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"快捷操作鍵"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"如要存取應用程式,請在鍵盤上按下快捷操作鍵。"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"恭喜!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三隻手指向上滑動並按住。輕按即可瞭解更多手勢。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用鍵盤查看所有應用程式"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"隨時按下快捷操作鍵。輕按即可瞭解更多手勢。"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"亮度滑桿現已加入超暗功能"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"而家只要將亮度校得越低,螢幕就會更加暗。\n\n由於亮度滑桿而家加入咗呢個功能,所以系統將會移除超暗功能捷徑。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"移除超暗功能捷徑"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"超暗功能捷徑已移除"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"裝置連接"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"無障礙功能"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"實用程式"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 93a99e6..787698c 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"喇叭和螢幕"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"建議的裝置"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"停止共用的工作階段,即可將媒體移至其他裝置"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"停止"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"廣播功能的運作方式"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"新增"</string>
     <string name="manage_users" msgid="1823875311934643849">"管理使用者"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"這項通知無法拖曳到分割畫面中。"</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"正在使用位置權限"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi 已關閉"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"優先模式"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"鬧鐘設定成功"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"自訂螢幕鎖定畫面"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"解鎖後即可自訂螢幕鎖定畫面"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"無法連上 Wi-Fi"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"正在使用位置權限"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"已封鎖攝影機"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"已封鎖攝影機和麥克風"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"已封鎖麥克風"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤操作"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"學習鍵盤快速鍵"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板操作"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"學習觸控板手勢、鍵盤快速鍵等"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返回手勢"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主畫面手勢"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"如要返回,請在觸控板的任何位置上用三指向左或向右滑動。\n\n使用快捷操作鍵 + ESC 鍵 (鍵盤快速鍵) 也可以返回。"</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"用 3 指從螢幕底部向上滑動,就能隨時返回主畫面。"</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"太棒了!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"你已完成「返回主畫面」手勢的教學課程。"</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近使用的應用程式"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"在觸控板上用三指向上滑動並按住。"</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢的教學課程。"</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"快捷操作鍵"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"如要存取應用程式,請按下鍵盤上的快捷操作鍵。"</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"恭喜!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三指向上滑動並按住。輕觸即可進一步瞭解手勢。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用鍵盤查看所有應用程式"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"你隨時可以按下快捷操作鍵。輕觸即可進一步瞭解手勢。"</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"「超暗」已併入亮度滑桿"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"現在只要將亮度調得更低,螢幕就會變得更暗。\n\n這項功能已併入亮度滑桿,因此系統將移除「超暗」捷徑。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"移除「超暗」捷徑"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"「超暗」捷徑已移除"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"連線"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"無障礙"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"公用程式"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9fc1166..3205b0e 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1179,6 +1179,10 @@
     <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
     <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Izipikha Neziboniso"</string>
     <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Amadivayisi Aphakanyisiwe"</string>
+    <!-- no translation found for media_input_group_title (2057057473860783021) -->
+    <skip />
+    <!-- no translation found for media_output_group_title (6789001895863332576) -->
+    <skip />
     <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Misa iseshini yakho eyabiwe ukuze uhambise imidiya kwenye idivayisi"</string>
     <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Misa"</string>
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Indlela ukusakaza okusebenza ngayo"</string>
@@ -1289,8 +1293,7 @@
     <string name="add" msgid="81036585205287996">"Faka"</string>
     <string name="manage_users" msgid="1823875311934643849">"Phatha abasebenzisi"</string>
     <string name="drag_split_not_supported" msgid="7173481676120546121">"Lesi saziso asikusekeli ukuhudulela ekuhlukaniseni isikrini."</string>
-    <!-- no translation found for dream_overlay_location_active (6484763493158166618) -->
-    <skip />
+    <string name="dream_overlay_location_active" msgid="6484763493158166618">"Indawo iyasebenza"</string>
     <string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"I-Wi-Fi ayitholakali"</string>
     <string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Imodi ebalulekile"</string>
     <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"I-alamu isethiwe"</string>
@@ -1347,8 +1350,7 @@
     <string name="lock_screen_settings" msgid="6152703934761402399">"Yenza ngokwezifiso ukukhiya isikrini"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"Vula ukuze wenze ukuvala isikrini ngendlela oyifisayo"</string>
     <string name="wifi_unavailable_dream_overlay_content_description" msgid="2024166212194640100">"I-Wi-Fi ayitholakali"</string>
-    <!-- no translation found for location_active_dream_overlay_content_description (6208885541020673916) -->
-    <skip />
+    <string name="location_active_dream_overlay_content_description" msgid="6208885541020673916">"Indawo iyasebenza"</string>
     <string name="camera_blocked_dream_overlay_content_description" msgid="4074759493559418130">"Ikhamera ivinjiwe"</string>
     <string name="camera_and_microphone_blocked_dream_overlay_content_description" msgid="7891078093416249764">"Ikhamera nemakrofoni zivinjiwe"</string>
     <string name="microphone_blocked_dream_overlay_content_description" msgid="5466897982130007033">"Imakrofoni ivinjiwe"</string>
@@ -1396,6 +1398,8 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
+    <!-- no translation found for shortcut_helper_content_description_drag_handle (5092426406009848110) -->
+    <skip />
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Funa usebenzisa ikhibhodi yakho"</string>
     <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Funda izinqamuleli zamakhibhodi"</string>
     <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Funa usebenzisa iphedi yokuthinta"</string>
@@ -1404,8 +1408,7 @@
     <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Funda ukunyakaza kwephedi yokuthinta, izinqamuleli zamakhibhodi, nokuningi"</string>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Ukunyakazisa umzimba kwangemuva"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Ukunyakazisa umzimba kwasekhaya"</string>
-    <!-- no translation found for touchpad_tutorial_recent_apps_gesture_button (8919227647650347359) -->
-    <skip />
+    <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Buka ama-app akamuva"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kwenziwe"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Buyela emuva"</string>
     <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Ukuze ubuyele emuva, swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu noma yikuphi ephedini yokuthinta.\n\nUngasebenzisa nesinqamuleli sekhibhodi Isenzo + ESC kulokhu."</string>
@@ -1415,14 +1418,10 @@
     <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Ukuze uye esikrinini sakho sasekhaya nganoma isiphi isikhathi, swayipha uye phezulu ngeminwe emithathu usuka phansi esikrinini sakho."</string>
     <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Kuhle!"</string>
     <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ukuqedile ukuthinta kokuya ekhaya."</string>
-    <!-- no translation found for touchpad_recent_apps_gesture_action_title (934906836867137906) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_guidance (6012057247259983871) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_title (8481920554139332593) -->
-    <skip />
-    <!-- no translation found for touchpad_recent_apps_gesture_success_body (4334263906697493273) -->
-    <skip />
+    <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Buka ama-app akamuva"</string>
+    <string name="touchpad_recent_apps_gesture_guidance" msgid="6012057247259983871">"Swayiphela phezulu bese ubamba usebenzisa iminwe emithathu kuphedi yokuthinta."</string>
+    <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Umsebenzi omuhle!"</string>
+    <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Uqedele ukubuka ukuthinta kwama-app akamuva."</string>
     <string name="tutorial_action_key_title" msgid="2659466586996495447">"Inkinobho yokufinyelela"</string>
     <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ukuze ufinyelele ama-app wakho, cindezela inkinobho yokufinyelela kukhibhodi yakho."</string>
     <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Halala!"</string>
@@ -1446,14 +1445,10 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swayiphela phezulu bese uyabamba usebenzisa iminwe emithathu. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Sebenzisa ikhibhodi yakho ukubuka wonke ama-app"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Cindezela inkinobho yokufinyelela noma kunini. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_title (910988771011857460) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_description (4453123359258743230) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_button (3947537827396916005) -->
-    <skip />
-    <!-- no translation found for accessibility_deprecate_extra_dim_dialog_toast (165474092660941104) -->
-    <skip />
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Ukufiphala okwengeziwe manje sekuyingxenye yokukhanya kwesislayida"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Manje ungenza isikrini sifiphaze ngokwengeziwe ngokwehlisa izinga lokukhanya nakakhulu.\n\nNjengoba lesi sakhi manje sesiyingxenye yesilayida sokukhanya, izinqamuleli ezokufiphaza okwengeziwe ziyasuswa."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Susa izinqamuleli zokufiphaza okwengeziwe"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Izinqamuleli zokufiphaza okwengeziwe zisusiwe"</string>
     <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Ukuxhumana"</string>
     <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Ukufinyeleleka"</string>
     <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Okusetshenziswayo"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 00846cb..e94248d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1005,10 +1005,6 @@
     <dimen name="ksh_app_item_minimum_height">64dp</dimen>
     <dimen name="ksh_category_separator_margin">16dp</dimen>
 
-    <!-- New keyboard shortcut helper -->
-    <dimen name="shortcut_helper_width">412dp</dimen>
-    <dimen name="shortcut_helper_height">728dp</dimen>
-
     <!-- The size of corner radius of the arrow in the onboarding toast. -->
     <dimen name="recents_onboarding_toast_arrow_corner_radius">2dp</dimen>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ba3822b..75389b1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1473,6 +1473,16 @@
     <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
     <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
 
+    <!-- The text to show in the notifications shade when a mode is suppressing notifications. [CHAR LIMIT=100] -->
+    <string name="modes_suppressing_shade_text">
+        {count, plural, offset:1
+            =0 {No notifications}
+            =1 {Notifications paused by {mode}}
+            =2 {Notifications paused by {mode} and one other mode}
+            other {Notifications paused by {mode} and # other modes}
+        }
+    </string>
+
     <!-- Media projection permission dialog action text. [CHAR LIMIT=60] -->
     <string name="media_projection_action_text">Start now</string>
 
@@ -1482,7 +1492,7 @@
     <!-- Text which is shown in the expanded notification shade when there are currently no notifications visible that the user hasn't already seen. [CHAR LIMIT=30] -->
     <string name="no_unseen_notif_text">No new notifications</string>
 
-    <!-- Title of heads up notification for adaptive notifications user education. [CHAR LIMIT=50] -->
+    <!-- Title of heads up notification for adaptive notifications user education. [CHAR LIMIT=60] -->
     <string name="adaptive_notification_edu_hun_title">Notification cooldown is on</string>
 
     <!-- Text of heads up notification for adaptive notifications user education. [CHAR LIMIT=100] -->
@@ -3711,6 +3721,12 @@
          [CHAR LIMIT=NONE]
           -->
     <string name="shortcut_helper_key_combinations_or_separator">or</string>
+    <!-- Content description of the drag handle that allows to swipe to dismiss the shortcut helper.
+         The helper is a  component that shows the  user which keyboard shortcuts they can
+         use. The helper shows shortcuts in categories, which can be collapsed or expanded.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_content_description_drag_handle">Drag handle</string>
+
 
     <!-- Keyboard touchpad tutorial scheduler-->
     <!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index 2f2b10f..66c54a3 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -65,7 +65,7 @@
 
     <Constraint
         android:id="@+id/header_title"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginTop="20dp"
         android:layout_marginStart="@dimen/qs_media_padding"
@@ -87,13 +87,11 @@
         app:layout_constraintEnd_toStartOf="@id/header_artist"
         app:layout_constraintTop_toTopOf="@id/header_artist"
         app:layout_constraintBottom_toBottomOf="@id/header_artist"
-        app:layout_constraintVertical_bias="0"
-        app:layout_constraintHorizontal_bias="0"
-        app:layout_constraintHorizontal_chainStyle="packed" />
+        app:layout_constraintVertical_bias="0" />
 
     <Constraint
         android:id="@+id/header_artist"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/qs_media_padding"
         android:layout_marginBottom="@dimen/qs_media_padding"
@@ -102,6 +100,8 @@
         app:layout_constrainedWidth="true"
         app:layout_constraintStart_toEndOf="@id/media_explicit_indicator"
         app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintHorizontal_weight="1"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
         app:layout_constraintVertical_bias="0" />
 
     <Constraint
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
index 0140d52..19cc3bc5 100644
--- a/packages/SystemUI/res/xml/media_session_expanded.xml
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -58,7 +58,7 @@
 
     <Constraint
         android:id="@+id/header_title"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/qs_media_padding"
         android:layout_marginEnd="@dimen/qs_media_padding"
@@ -80,13 +80,11 @@
         app:layout_constraintStart_toStartOf="@id/header_title"
         app:layout_constraintEnd_toStartOf="@id/header_artist"
         app:layout_constraintTop_toTopOf="@id/header_artist"
-        app:layout_constraintBottom_toTopOf="@id/media_action_barrier_top"
-        app:layout_constraintHorizontal_bias="0"
-        app:layout_constraintHorizontal_chainStyle="packed"/>
+        app:layout_constraintBottom_toTopOf="@id/media_action_barrier_top" />
 
     <Constraint
         android:id="@+id/header_artist"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/qs_media_padding"
         android:layout_marginBottom="@dimen/qs_media_padding"
@@ -95,6 +93,8 @@
         app:layout_constraintEnd_toStartOf="@id/actionPlayPause"
         app:layout_constraintStart_toEndOf="@id/media_explicit_indicator"
         app:layout_constraintBottom_toTopOf="@id/media_action_barrier_top"
+        app:layout_constraintHorizontal_weight="1"
+        app:layout_constraintHorizontal_chainStyle="spread_inside"
         app:layout_constraintVertical_bias="0" />
 
     <Constraint
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 8f55961..0f1da50 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -70,6 +70,7 @@
         "jsr330",
         "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
         "//frameworks/libs/systemui:msdl",
+        "//frameworks/libs/systemui:view_capture",
     ],
     resource_dirs: [
         "res",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 87cc86f..8298397 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -32,6 +32,9 @@
 import com.android.systemui.plugins.PluginFragment;
 import com.android.systemui.plugins.PluginLifecycleManager;
 import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginProtector;
+import com.android.systemui.plugins.PluginWrapper;
+import com.android.systemui.plugins.ProtectedPluginListener;
 
 import dalvik.system.PathClassLoader;
 
@@ -49,7 +52,8 @@
  *
  * @param <T> The type of plugin that this contains.
  */
-public class PluginInstance<T extends Plugin> implements PluginLifecycleManager {
+public class PluginInstance<T extends Plugin>
+        implements PluginLifecycleManager, ProtectedPluginListener {
     private static final String TAG = "PluginInstance";
 
     private final Context mAppContext;
@@ -58,6 +62,7 @@
     private final PluginFactory<T> mPluginFactory;
     private final String mTag;
 
+    private boolean mHasError = false;
     private BiConsumer<String, String> mLogConsumer = null;
     private Context mPluginContext;
     private T mPlugin;
@@ -87,6 +92,11 @@
         return mTag;
     }
 
+    /** */
+    public boolean hasError() {
+        return mHasError;
+    }
+
     public void setLogFunc(BiConsumer logConsumer) {
         mLogConsumer = logConsumer;
     }
@@ -97,8 +107,21 @@
         }
     }
 
+    @Override
+    public synchronized boolean onFail(String className, String methodName, LinkageError failure) {
+        mHasError = true;
+        unloadPlugin();
+        mListener.onPluginDetached(this);
+        return true;
+    }
+
     /** Alerts listener and plugin that the plugin has been created. */
     public synchronized void onCreate() {
+        if (mHasError) {
+            log("Previous LinkageError detected for plugin class");
+            return;
+        }
+
         boolean loadPlugin = mListener.onPluginAttached(this);
         if (!loadPlugin) {
             if (mPlugin != null) {
@@ -109,13 +132,17 @@
         }
 
         if (mPlugin == null) {
-            log("onCreate auto-load");
+            log("onCreate: auto-load");
             loadPlugin();
             return;
         }
 
+        if (!checkVersion()) {
+            log("onCreate: version check failed");
+            return;
+        }
+
         log("onCreate: load callbacks");
-        mPluginFactory.checkVersion(mPlugin);
         if (!(mPlugin instanceof PluginFragment)) {
             // Only call onCreate for plugins that aren't fragments, as fragments
             // will get the onCreate as part of the fragment lifecycle.
@@ -126,6 +153,12 @@
 
     /** Alerts listener and plugin that the plugin is being shutdown. */
     public synchronized void onDestroy() {
+        if (mHasError) {
+            // Detached in error handler
+            log("onDestroy - no-op");
+            return;
+        }
+
         log("onDestroy");
         unloadPlugin();
         mListener.onPluginDetached(this);
@@ -134,28 +167,37 @@
     /** Returns the current plugin instance (if it is loaded). */
     @Nullable
     public T getPlugin() {
-        return mPlugin;
+        return mHasError ? null : mPlugin;
     }
 
     /**
      * Loads and creates the plugin if it does not exist.
      */
     public synchronized void loadPlugin() {
+        if (mHasError) {
+            log("Previous LinkageError detected for plugin class");
+            return;
+        }
+
         if (mPlugin != null) {
             log("Load request when already loaded");
             return;
         }
 
         // Both of these calls take about 1 - 1.5 seconds in test runs
-        mPlugin = mPluginFactory.createPlugin();
+        mPlugin = mPluginFactory.createPlugin(this);
         mPluginContext = mPluginFactory.createPluginContext();
         if (mPlugin == null || mPluginContext == null) {
             Log.e(mTag, "Requested load, but failed");
             return;
         }
 
+        if (!checkVersion()) {
+            log("loadPlugin: version check failed");
+            return;
+        }
+
         log("Loaded plugin; running callbacks");
-        mPluginFactory.checkVersion(mPlugin);
         if (!(mPlugin instanceof PluginFragment)) {
             // Only call onCreate for plugins that aren't fragments, as fragments
             // will get the onCreate as part of the fragment lifecycle.
@@ -165,6 +207,29 @@
     }
 
     /**
+     * Checks the plugin version, and permanently destroys the plugin instance on a failure
+     */
+    private synchronized boolean checkVersion() {
+        if (mHasError) {
+            return false;
+        }
+
+        if (mPlugin == null) {
+            return true;
+        }
+
+        if (mPluginFactory.checkVersion(mPlugin)) {
+            return true;
+        }
+
+        Log.wtf(TAG, "Version check failed for " + mPlugin.getClass().getSimpleName());
+        mHasError = true;
+        unloadPlugin();
+        mListener.onPluginDetached(this);
+        return false;
+    }
+
+    /**
      * Unloads and destroys the current plugin instance if it exists.
      *
      * This will free the associated memory if there are not other references.
@@ -204,7 +269,7 @@
     }
 
     public VersionInfo getVersionInfo() {
-        return mPluginFactory.checkVersion(mPlugin);
+        return mPluginFactory.getVersionInfo(mPlugin);
     }
 
     @VisibleForTesting
@@ -295,16 +360,19 @@
 
     /** Class that compares a plugin class against an implementation for version matching. */
     public interface VersionChecker {
-        /** Compares two plugin classes. */
-        <T extends Plugin> VersionInfo checkVersion(
+        /** Compares two plugin classes. Returns true when match. */
+        <T extends Plugin> boolean checkVersion(
                 Class<T> instanceClass, Class<T> pluginClass, Plugin plugin);
+
+        /** Returns VersionInfo for the target class */
+        <T extends Plugin> VersionInfo getVersionInfo(Class<T> instanceclass);
     }
 
     /** Class that compares a plugin class against an implementation for version matching. */
     public static class VersionCheckerImpl implements VersionChecker {
         @Override
         /** Compares two plugin classes. */
-        public <T extends Plugin> VersionInfo checkVersion(
+        public <T extends Plugin> boolean checkVersion(
                 Class<T> instanceClass, Class<T> pluginClass, Plugin plugin) {
             VersionInfo pluginVersion = new VersionInfo().addClass(pluginClass);
             VersionInfo instanceVersion = new VersionInfo().addClass(instanceClass);
@@ -313,11 +381,17 @@
             } else if (plugin != null) {
                 int fallbackVersion = plugin.getVersion();
                 if (fallbackVersion != pluginVersion.getDefaultVersion()) {
-                    throw new VersionInfo.InvalidVersionException("Invalid legacy version", false);
+                    return false;
                 }
-                return null;
             }
-            return instanceVersion;
+            return true;
+        }
+
+        @Override
+        /** Returns the version info for the class */
+        public <T extends Plugin> VersionInfo getVersionInfo(Class<T> instanceClass) {
+            VersionInfo instanceVersion = new VersionInfo().addClass(instanceClass);
+            return instanceVersion.hasVersionInfo() ? instanceVersion : null;
         }
     }
 
@@ -364,20 +438,16 @@
         }
 
         /** Creates the related plugin object from the factory */
-        public T createPlugin() {
+        public T createPlugin(ProtectedPluginListener listener) {
             try {
                 ClassLoader loader = mClassLoaderFactory.get();
                 Class<T> instanceClass = (Class<T>) Class.forName(
                         mComponentName.getClassName(), true, loader);
                 T result = (T) mInstanceFactory.create(instanceClass);
                 Log.v(TAG, "Created plugin: " + result);
-                return result;
-            } catch (ClassNotFoundException ex) {
-                Log.e(TAG, "Failed to load plugin", ex);
-            } catch (IllegalAccessException ex) {
-                Log.e(TAG, "Failed to load plugin", ex);
-            } catch (InstantiationException ex) {
-                Log.e(TAG, "Failed to load plugin", ex);
+                return PluginProtector.protectIfAble(result, listener);
+            } catch (ReflectiveOperationException ex) {
+                Log.wtf(TAG, "Failed to load plugin", ex);
             }
             return null;
         }
@@ -394,13 +464,27 @@
             return null;
         }
 
-        /** Check Version and create VersionInfo for instance */
-        public VersionInfo checkVersion(T instance) {
+        /** Check Version for the instance */
+        public boolean checkVersion(T instance) {
             if (instance == null) {
-                instance = createPlugin();
+                instance = createPlugin(null);
+            }
+            if (instance instanceof PluginWrapper) {
+                instance = ((PluginWrapper<T>) instance).getPlugin();
             }
             return mVersionChecker.checkVersion(
                     (Class<T>) instance.getClass(), mPluginClass, instance);
         }
+
+        /** Get Version Info for the instance */
+        public VersionInfo getVersionInfo(T instance) {
+            if (instance == null) {
+                instance = createPlugin(null);
+            }
+            if (instance instanceof PluginWrapper) {
+                instance = ((PluginWrapper<T>) instance).getPlugin();
+            }
+            return mVersionChecker.getVersionInfo((Class<T>) instance.getClass());
+        }
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index f358ba2..4db6ab6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.shared.rotation;
 
+import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+
 import android.annotation.DimenRes;
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
@@ -30,7 +33,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
@@ -38,6 +40,7 @@
 import androidx.annotation.BoolRes;
 import androidx.core.view.OneShotPreDrawListener;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
 
 /**
@@ -47,7 +50,7 @@
 
     private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300;
 
-    private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mWindowManager;
     private final ViewGroup mKeyButtonContainer;
     private final FloatingRotationButtonView mKeyButtonView;
 
@@ -88,7 +91,8 @@
             @DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
             @DimenRes int rippleMaxWidth, @BoolRes int floatingRotationBtnPositionLeftResource) {
         mContext = context;
-        mWindowManager = mContext.getSystemService(WindowManager.class);
+        mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext,
+                enableViewCaptureTracing());
         mKeyButtonContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(layout, null);
         mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
         mKeyButtonView.setVisibility(View.VISIBLE);
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 121577e..78cd02f 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
@@ -345,6 +345,10 @@
                 || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0) {
             return false;
         }
+        // Disable back gesture on the hub, but not when the shade is showing.
+        if ((sysuiStateFlags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
+            return (sysuiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) == 0;
+        }
         if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
             sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index c1eae2e..e5cf7cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,12 +15,15 @@
  */
 package com.android.keyguard
 
+import android.app.NotificationManager.zenModeFromInterruptionFilter
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.res.Resources
 import android.os.Trace
+import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+import android.provider.Settings.Global.ZEN_MODE_OFF
 import android.text.format.DateFormat
 import android.util.Log
 import android.util.TypedValue
@@ -49,6 +52,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.log.core.Logger
+import com.android.systemui.modes.shared.ModesUi
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockFaceController
@@ -63,6 +67,7 @@
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
 import com.android.systemui.util.concurrency.DelayableExecutor
 import java.util.Locale
 import java.util.TimeZone
@@ -97,12 +102,13 @@
     private val clockBuffers: ClockMessageBuffers,
     private val featureFlags: FeatureFlagsClassic,
     private val zenModeController: ZenModeController,
+    private val zenModeInteractor: ZenModeInteractor,
 ) {
     var loggers =
         listOf(
                 clockBuffers.infraMessageBuffer,
                 clockBuffers.smallClockMessageBuffer,
-                clockBuffers.largeClockMessageBuffer
+                clockBuffers.largeClockMessageBuffer,
             )
             .map { Logger(it, TAG) }
 
@@ -146,7 +152,7 @@
                         bgExecutor,
                         regionSamplingEnabled,
                         isLockscreen = true,
-                        ::updateColors
+                        ::updateColors,
                     )
                     .apply { startRegionSampler() }
 
@@ -157,7 +163,7 @@
                         bgExecutor,
                         regionSamplingEnabled,
                         isLockscreen = true,
-                        ::updateColors
+                        ::updateColors,
                     )
                     .apply { startRegionSampler() }
 
@@ -271,7 +277,7 @@
         bgExecutor: Executor?,
         regionSamplingEnabled: Boolean,
         isLockscreen: Boolean,
-        updateColors: () -> Unit
+        updateColors: () -> Unit,
     ): RegionSampler {
         return RegionSampler(
             sampledView,
@@ -384,24 +390,30 @@
             }
         }
 
+    @VisibleForTesting
+    internal fun listenForDnd(scope: CoroutineScope): Job {
+        ModesUi.assertInNewMode()
+        return scope.launch {
+            zenModeInteractor.dndMode.collect {
+                val zenMode =
+                    if (it != null && it.isActive)
+                        zenModeFromInterruptionFilter(
+                            it.interruptionFilter,
+                            ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                        )
+                    else ZEN_MODE_OFF
+
+                handleZenMode(zenMode)
+            }
+        }
+    }
+
     private val zenModeCallback =
         object : ZenModeController.Callback {
             override fun onZenChanged(zen: Int) {
-                var mode = ZenMode.fromInt(zen)
-                if (mode == null) {
-                    Log.e(TAG, "Failed to get zen mode from int: $zen")
-                    return
+                if (!ModesUi.isEnabled) {
+                    handleZenMode(zen)
                 }
-
-                zenData =
-                    ZenData(
-                            mode,
-                            if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
-                            else SysuiR.string::dnd_is_on.name
-                        )
-                        .also { data ->
-                            mainExecutor.execute { clock?.run { events.onZenDataChanged(data) } }
-                        }
             }
 
             override fun onNextAlarmChanged() {
@@ -409,7 +421,7 @@
                 alarmData =
                     AlarmData(
                             if (nextAlarmMillis > 0) nextAlarmMillis else null,
-                            SysuiR.string::status_bar_alarm.name
+                            SysuiR.string::status_bar_alarm.name,
                         )
                         .also { data ->
                             mainExecutor.execute { clock?.run { events.onAlarmDataChanged(data) } }
@@ -417,6 +429,24 @@
             }
         }
 
+    private fun handleZenMode(zen: Int) {
+        val mode = ZenMode.fromInt(zen)
+        if (mode == null) {
+            Log.e(TAG, "Failed to get zen mode from int: $zen")
+            return
+        }
+
+        zenData =
+            ZenData(
+                    mode,
+                    if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
+                    else SysuiR.string::dnd_is_on.name,
+                )
+                .also { data ->
+                    mainExecutor.execute { clock?.run { events.onZenDataChanged(data) } }
+                }
+    }
+
     fun registerListeners(parent: View) {
         if (isRegistered) {
             return
@@ -424,7 +454,7 @@
         isRegistered = true
         broadcastDispatcher.registerReceiver(
             localeBroadcastReceiver,
-            IntentFilter(Intent.ACTION_LOCALE_CHANGED)
+            IntentFilter(Intent.ACTION_LOCALE_CHANGED),
         )
         configurationController.addCallback(configListener)
         batteryController.addCallback(batteryCallback)
@@ -434,6 +464,9 @@
             parent.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.CREATED) {
                     listenForDozing(this)
+                    if (ModesUi.isEnabled) {
+                        listenForDnd(this)
+                    }
                     if (MigrateClocksToBlueprint.isEnabled) {
                         listenForDozeAmountTransition(this)
                         listenForAnyStateToAodTransition(this)
@@ -449,7 +482,9 @@
 
         bgExecutor.execute {
             // Query ZenMode data
-            zenModeCallback.onZenChanged(zenModeController.zen)
+            if (!ModesUi.isEnabled) {
+                zenModeCallback.onZenChanged(zenModeController.zen)
+            }
             zenModeCallback.onNextAlarmChanged()
         }
     }
@@ -605,10 +640,9 @@
     @VisibleForTesting
     internal fun listenForDozing(scope: CoroutineScope): Job {
         return scope.launch {
-            combine(
-                    keyguardInteractor.dozeAmount,
-                    keyguardInteractor.isDozing,
-                ) { localDozeAmount, localIsDozing ->
+            combine(keyguardInteractor.dozeAmount, keyguardInteractor.isDozing) {
+                    localDozeAmount,
+                    localIsDozing ->
                     localDozeAmount > dozeAmount || localIsDozing
                 }
                 .collect { localIsDozing -> isDozing = localIsDozing }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ffbc85c..f05cbf4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -109,6 +109,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /** Determines how the bouncer is displayed to the user. */
 public class KeyguardSecurityContainer extends ConstraintLayout {
@@ -170,6 +172,7 @@
     private ViewMode mViewMode = new DefaultViewMode();
     private boolean mIsInteractable;
     protected ViewMediatorCallback mViewMediatorCallback;
+    private Executor mBgExecutor;
     /*
      * Using MODE_UNINITIALIZED to mean the view mode is set to DefaultViewMode, but init() has not
      * yet been called on it. This will happen when the ViewController is initialized.
@@ -352,6 +355,10 @@
         updateBiometricRetry(securityMode, faceAuthEnabled);
     }
 
+    void setBackgroundExecutor(Executor bgExecutor) {
+        mBgExecutor = bgExecutor;
+    }
+
     void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingManager falsingManager,
             UserSwitcherController userSwitcherController,
             UserSwitcherViewMode.UserSwitcherCallback userSwitcherCallback,
@@ -367,7 +374,7 @@
                 mViewMode = new OneHandedViewMode();
                 break;
             case MODE_USER_SWITCHER:
-                mViewMode = new UserSwitcherViewMode(userSwitcherCallback);
+                mViewMode = new UserSwitcherViewMode(userSwitcherCallback, mBgExecutor);
                 break;
             default:
                 mViewMode = new DefaultViewMode();
@@ -991,6 +998,7 @@
         private FalsingManager mFalsingManager;
         private UserSwitcherController mUserSwitcherController;
         private KeyguardUserSwitcherPopupMenu mPopup;
+        private Executor mBgExecutor;
         private Resources mResources;
         private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
                 this::setupUserSwitcher;
@@ -998,8 +1006,9 @@
         private UserSwitcherCallback mUserSwitcherCallback;
         private FalsingA11yDelegate mFalsingA11yDelegate;
 
-        UserSwitcherViewMode(UserSwitcherCallback userSwitcherCallback) {
+        UserSwitcherViewMode(UserSwitcherCallback userSwitcherCallback, Executor bgExecutor) {
             mUserSwitcherCallback = userSwitcherCallback;
+            mBgExecutor = bgExecutor;
         }
 
         @Override
@@ -1068,18 +1077,22 @@
             mView.removeView(mUserSwitcher);
         }
 
-        private Drawable findLargeUserIcon(int userId) {
-            Bitmap userIcon = UserManager.get(mView.getContext()).getUserIcon(userId);
-            if (userIcon != null) {
-                int iconSize =
-                        mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_icon_size);
-                return CircleFramedDrawable.getInstance(
-                    mView.getContext(),
-                    Icon.scaleDownIfNecessary(userIcon, iconSize, iconSize)
-                );
-            }
-
-            return UserIcons.getDefaultUserIcon(mResources, userId, false);
+        private void findLargeUserIcon(int userId, Consumer<Drawable> consumer) {
+            mBgExecutor.execute(() -> {
+                Drawable icon;
+                Bitmap userIcon = UserManager.get(mView.getContext()).getUserIcon(userId);
+                if (userIcon != null) {
+                    int iconSize = mResources.getDimensionPixelSize(
+                            R.dimen.bouncer_user_switcher_icon_size);
+                    icon = CircleFramedDrawable.getInstance(
+                        mView.getContext(),
+                        Icon.scaleDownIfNecessary(userIcon, iconSize, iconSize)
+                    );
+                } else  {
+                    icon = UserIcons.getDefaultUserIcon(mResources, userId, false);
+                }
+                consumer.accept(icon);
+            });
         }
 
         @Override
@@ -1136,8 +1149,15 @@
                 return;
             }
             final String currentUserName = mUserSwitcherController.getCurrentUserName();
-            Drawable userIcon = findLargeUserIcon(currentUser.info.id);
-            ((ImageView) mView.findViewById(R.id.user_icon)).setImageDrawable(userIcon);
+            findLargeUserIcon(currentUser.info.id,
+                    (Drawable userIcon) -> {
+                        mView.post(() -> {
+                            ImageView view = (ImageView) mView.findViewById(R.id.user_icon);
+                            if (view != null) {
+                                view.setImageDrawable(userIcon);
+                            }
+                        });
+                });
             mUserSwitcher.setText(currentUserName);
 
             KeyguardUserSwitcherAnchor anchor = mView.findViewById(R.id.user_switcher_anchor);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 2d28a18..a408211 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -76,6 +76,7 @@
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.FalsingA11yDelegate;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.flags.FeatureFlags;
@@ -102,6 +103,7 @@
 
 import java.io.File;
 import java.util.Arrays;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
@@ -426,6 +428,7 @@
     private final Provider<JavaAdapter> mJavaAdapter;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final Lazy<PrimaryBouncerInteractor> mPrimaryBouncerInteractor;
+    private final Executor mBgExecutor;
     @Nullable
     private Job mSceneTransitionCollectionJob;
 
@@ -459,6 +462,7 @@
             DevicePolicyManager devicePolicyManager,
             KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor,
             Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor,
+            @Background Executor bgExecutor,
             Provider<DeviceEntryInteractor> deviceEntryInteractor
     ) {
         super(view);
@@ -493,11 +497,13 @@
         mDeviceProvisionedController = deviceProvisionedController;
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
         mDevicePolicyManager = devicePolicyManager;
+        mBgExecutor = bgExecutor;
     }
 
     @Override
     public void onInit() {
         mSecurityViewFlipperController.init();
+        mView.setBackgroundExecutor(mBgExecutor);
         updateResources();
         configureMode();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 0898134..76df9c9 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -25,7 +25,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.RemoteException;
-import android.util.Log;
 import android.view.GestureDetector;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWindowManager;
@@ -76,10 +75,9 @@
  * touches are consumed.
  */
 public class TouchMonitor {
+    private final Logger mLogger;
     // This executor is used to protect {@code mActiveTouchSessions} from being modified
     // concurrently. Any operation that adds or removes values should use this executor.
-    public String TAG = "DreamOverlayTouchMonitor";
-    private final Logger mLogger;
     private final Executor mMainExecutor;
     private final Executor mBackgroundExecutor;
 
@@ -298,13 +296,12 @@
                     mWindowManagerService.registerSystemGestureExclusionListener(
                             mGestureExclusionListener, mDisplayId);
                 } catch (RemoteException e) {
-                    // Handle the exception
-                    Log.e(TAG, "Failed to register gesture exclusion listener", e);
+                    mLogger.e("Failed to register gesture exclusion listener", e);
                 }
             });
         }
         mCurrentInputSession = mInputSessionFactory.create(
-                        "dreamOverlay",
+                        mLoggingName,
                         mInputEventListener,
                         mOnGestureListener,
                         true)
@@ -326,7 +323,7 @@
                     }
                 } catch (RemoteException e) {
                     // Handle the exception
-                    Log.e(TAG, "unregisterSystemGestureExclusionListener: failed", e);
+                    mLogger.e("unregisterSystemGestureExclusionListener: failed", e);
                 }
             });
         }
@@ -543,6 +540,7 @@
     private InputSession mCurrentInputSession;
     private final int mDisplayId;
     private final IWindowManager mWindowManagerService;
+    private final String mLoggingName;
 
     private Rect mMaxBounds;
 
@@ -589,7 +587,8 @@
         mDisplayHelper = displayHelper;
         mWindowManagerService = windowManagerService;
         mConfigurationInteractor = configurationInteractor;
-        mLogger = new Logger(logBuffer, loggingName + ":TouchMonitor");
+        mLoggingName = loggingName + ":TouchMonitor";
+        mLogger = new Logger(logBuffer, mLoggingName);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 5bd7e54..23045a3 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -47,7 +47,9 @@
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
     private val shadeController: ShadeController,
     private val notificationShadeWindowController: NotificationShadeWindowController,
-    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
+    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
+    private val shadeBackActionInteractor: ShadeBackActionInteractor,
+    private val qsController: QuickSettingsController,
 ) : CoreStartable {
 
     private var isCallbackRegistered = false
@@ -77,14 +79,6 @@
         get() =
             notificationShadeWindowController.windowRootView?.viewRootImpl?.onBackInvokedDispatcher
 
-    private lateinit var shadeBackActionInteractor: ShadeBackActionInteractor
-    private lateinit var qsController: QuickSettingsController
-
-    fun setup(qsController: QuickSettingsController, svController: ShadeBackActionInteractor) {
-        this.qsController = qsController
-        this.shadeBackActionInteractor = svController
-    }
-
     override fun start() {
         scope.launch {
             windowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index b10d37e..c95a94e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -34,9 +34,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags.lightRevealMigration
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
-import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.biometrics.shared.model.toSensorType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
@@ -104,7 +102,6 @@
 
     private var udfpsController: UdfpsController? = null
     private var udfpsRadius: Float = -1f
-    private var udfpsType: FingerprintSensorType = FingerprintSensorType.UNKNOWN
 
     override fun start() {
         init()
@@ -373,11 +370,8 @@
     private val udfpsControllerCallback =
         object : UdfpsController.Callback {
             override fun onFingerDown() {
-                // only show dwell ripple for device entry non-ultrasonic udfps
-                if (
-                    keyguardUpdateMonitor.isFingerprintDetectionRunning &&
-                        udfpsType != FingerprintSensorType.UDFPS_ULTRASONIC
-                ) {
+                // only show dwell ripple for device entry
+                if (keyguardUpdateMonitor.isFingerprintDetectionRunning) {
                     showDwellRipple()
                 }
             }
@@ -403,7 +397,6 @@
             if (it.size > 0) {
                 udfpsController = udfpsControllerProvider.get()
                 udfpsRadius = authController.udfpsRadius
-                udfpsType = it[0].sensorType.toSensorType()
 
                 if (mView.isAttachedToWindow) {
                     udfpsController?.addCallback(udfpsControllerCallback)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 5ffb9ab2..a3904ca 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -19,6 +19,7 @@
 import static android.app.StatusBarManager.SESSION_BIOMETRIC_PROMPT;
 import static android.app.StatusBarManager.SESSION_KEYGUARD;
 import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
 import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP;
 import static android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD;
 import static android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING;
@@ -329,6 +330,22 @@
                 int sensorId,
                 @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo
         ) {
+            if (isUltrasonic()) {
+                if (acquiredInfo == FINGERPRINT_ACQUIRED_START) {
+                    mFgExecutor.execute(() -> {
+                        for (Callback cb : mCallbacks) {
+                            cb.onFingerDown();
+                        }
+                    });
+                } else {
+                    mFgExecutor.execute(() -> {
+                        for (Callback cb : mCallbacks) {
+                            cb.onFingerUp();
+                        }
+                    });
+                }
+            }
+
             if (BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquiredInfo)) {
                 boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD;
                 mFgExecutor.execute(() -> {
@@ -1024,6 +1041,10 @@
         return mSensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
     }
 
+    private boolean isUltrasonic() {
+        return mSensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
+    }
+
     public boolean isFingerDown() {
         return mOnFingerDown;
     }
@@ -1105,8 +1126,10 @@
             }
         }
 
-        for (Callback cb : mCallbacks) {
-            cb.onFingerDown();
+        if (isOptical()) {
+            for (Callback cb : mCallbacks) {
+                cb.onFingerDown();
+            }
         }
     }
 
@@ -1143,8 +1166,10 @@
         if (mOnFingerDown) {
             mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
                     y, minor, major, orientation, time, gestureStart, isAod);
-            for (Callback cb : mCallbacks) {
-                cb.onFingerUp();
+            if (isOptical()) {
+                for (Callback cb : mCallbacks) {
+                    cb.onFingerUp();
+                }
             }
         }
         mOnFingerDown = false;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
index 7ecbb88..ec3fd9f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
@@ -25,8 +25,6 @@
 import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl
 import com.android.systemui.dagger.SysUISingleton
 import dagger.Binds
 import dagger.Module
@@ -48,12 +46,6 @@
 
     @Binds
     @SysUISingleton
-    fun providesSideFpsOverlayInteractor(
-        impl: SideFpsOverlayInteractorImpl
-    ): SideFpsOverlayInteractor
-
-    @Binds
-    @SysUISingleton
     fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt
deleted file mode 100644
index 10c3483..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.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.biometrics.domain.interactor
-
-import android.util.Log
-import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
-import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-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.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.onEach
-
-/** Encapsulates business logic for showing and hiding the side fingerprint sensor indicator. */
-interface SideFpsOverlayInteractor {
-    /** Whether the side fingerprint sensor indicator is currently showing. */
-    val isShowing: Flow<Boolean>
-}
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class SideFpsOverlayInteractorImpl
-@Inject
-constructor(
-    biometricStatusInteractor: BiometricStatusInteractor,
-    displayStateInteractor: DisplayStateInteractor,
-    deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor,
-    sfpsSensorInteractor: SideFpsSensorInteractor,
-    // TODO(b/365182034): add progress bar input when rest to unlock feature is implemented
-) : SideFpsOverlayInteractor {
-    private val sfpsOverlayEnabled: Flow<Boolean> =
-        sfpsSensorInteractor.isAvailable.sample(displayStateInteractor.isInRearDisplayMode) {
-            isAvailable: Boolean,
-            isInRearDisplayMode: Boolean ->
-            isAvailable && !isInRearDisplayMode
-        }
-
-    private val showSideFpsOverlay: Flow<Boolean> =
-        combine(
-            biometricStatusInteractor.sfpsAuthenticationReason,
-            deviceEntrySideFpsOverlayInteractor.showIndicatorForDeviceEntry,
-            // TODO(b/365182034): add progress bar input when rest to unlock feature is implemented
-        ) { systemServerAuthReason, showIndicatorForDeviceEntry ->
-            Log.d(
-                TAG,
-                "systemServerAuthReason = $systemServerAuthReason, " +
-                    "showIndicatorForDeviceEntry = $showIndicatorForDeviceEntry, "
-            )
-            systemServerAuthReason != NotRunning || showIndicatorForDeviceEntry
-        }
-
-    override val isShowing: Flow<Boolean> =
-        sfpsOverlayEnabled
-            .flatMapLatest { sfpsOverlayEnabled ->
-                if (!sfpsOverlayEnabled) {
-                    flowOf(false)
-                } else {
-                    showSideFpsOverlay
-                }
-            }
-            .onEach { Log.d(TAG, "isShowing: $it") }
-
-    companion object {
-        private const val TAG = "SideFpsOverlayInteractor"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index bb450c0..18a7739 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -83,7 +83,7 @@
 
     /** Sets whether Udfps overlay should handle touches */
     fun setHandleTouches(shouldHandle: Boolean = true) {
-        if (authController.isUltrasonicUdfpsSupported
+        if (authController.isUdfpsSupported
                 && shouldHandle != _shouldHandleTouches.value) {
             fingerprintManager?.setIgnoreDisplayTouches(
                 requestId.value,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index d055731..73f75a4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -18,11 +18,13 @@
 
 import android.animation.Animator
 import android.animation.AnimatorSet
+import android.animation.ValueAnimator
 import android.graphics.Outline
 import android.graphics.Rect
 import android.transition.AutoTransition
 import android.transition.TransitionManager
 import android.util.TypedValue
+import android.view.Surface
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewOutlineProvider
@@ -158,13 +160,16 @@
             fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
                 viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
                 largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+                largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
                 largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
                 largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
 
                 if (hideSensorIcon) {
                     smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+                    smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
                     smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
                     mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+                    mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
                     mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
                 }
             }
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 9fe1dc5..9578da4 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
@@ -33,44 +33,89 @@
 import com.android.app.animation.Interpolators
 import com.android.keyguard.KeyguardPINView
 import com.android.systemui.CoreStartable
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
+import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason.NotRunning
 import com.android.systemui.biometrics.shared.model.LottieCallback
 import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.sample
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 /** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class SideFpsOverlayViewBinder
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
     @Application private val applicationContext: Context,
+    private val biometricStatusInteractor: Lazy<BiometricStatusInteractor>,
+    private val displayStateInteractor: Lazy<DisplayStateInteractor>,
+    private val deviceEntrySideFpsOverlayInteractor: Lazy<DeviceEntrySideFpsOverlayInteractor>,
     private val layoutInflater: Lazy<LayoutInflater>,
-    private val sideFpsOverlayInteractor: Lazy<SideFpsOverlayInteractor>,
-    private val sideFpsOverlayViewModel: Lazy<SideFpsOverlayViewModel>,
+    private val sideFpsProgressBarViewModel: Lazy<SideFpsProgressBarViewModel>,
+    private val sfpsSensorInteractor: Lazy<SideFpsSensorInteractor>,
     private val windowManager: Lazy<WindowManager>
 ) : CoreStartable {
-    private var overlayView: View? = null
 
     override fun start() {
-        applicationScope.launch {
-            sideFpsOverlayInteractor.get().isShowing.collect { isShowing: Boolean ->
-                if (isShowing) {
-                    show()
-                } else {
-                    hide()
+        applicationScope
+            .launch {
+                sfpsSensorInteractor.get().isAvailable.collect { isSfpsAvailable ->
+                    if (isSfpsAvailable) {
+                        combine(
+                                biometricStatusInteractor.get().sfpsAuthenticationReason,
+                                deviceEntrySideFpsOverlayInteractor
+                                    .get()
+                                    .showIndicatorForDeviceEntry,
+                                sideFpsProgressBarViewModel.get().isVisible,
+                                ::Triple
+                            )
+                            .sample(displayStateInteractor.get().isInRearDisplayMode, ::Pair)
+                            .collect { (combinedFlows, isInRearDisplayMode: Boolean) ->
+                                val (
+                                    systemServerAuthReason,
+                                    showIndicatorForDeviceEntry,
+                                    progressBarIsVisible) =
+                                    combinedFlows
+                                Log.d(
+                                    TAG,
+                                    "systemServerAuthReason = $systemServerAuthReason, " +
+                                        "showIndicatorForDeviceEntry = " +
+                                        "$showIndicatorForDeviceEntry, " +
+                                        "progressBarIsVisible = $progressBarIsVisible"
+                                )
+                                if (!isInRearDisplayMode) {
+                                    if (progressBarIsVisible) {
+                                        hide()
+                                    } else if (systemServerAuthReason != NotRunning) {
+                                        show()
+                                    } else if (showIndicatorForDeviceEntry) {
+                                        show()
+                                    } else {
+                                        hide()
+                                    }
+                                }
+                            }
+                    }
                 }
             }
-        }
     }
 
+    private var overlayView: View? = null
+
     /** Show the side fingerprint sensor indicator */
     private fun show() {
         if (overlayView?.isAttachedToWindow == true) {
@@ -80,10 +125,17 @@
             )
             return
         }
-        overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
-        val overlayViewModel = sideFpsOverlayViewModel.get()
-        bind(overlayView!!, overlayViewModel, windowManager.get())
 
+        overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
+
+        val overlayViewModel =
+            SideFpsOverlayViewModel(
+                applicationContext,
+                deviceEntrySideFpsOverlayInteractor.get(),
+                displayStateInteractor.get(),
+                sfpsSensorInteractor.get(),
+            )
+        bind(overlayView!!, overlayViewModel, windowManager.get())
         overlayView!!.visibility = View.INVISIBLE
         Log.d(TAG, "show(): adding overlayView $overlayView")
         windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
@@ -109,20 +161,6 @@
     companion object {
         private const val TAG = "SideFpsOverlayViewBinder"
 
-        private val accessibilityDelegate =
-            object : View.AccessibilityDelegate() {
-                override fun dispatchPopulateAccessibilityEvent(
-                    host: View,
-                    event: AccessibilityEvent
-                ): Boolean {
-                    return if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-                        true
-                    } else {
-                        super.dispatchPopulateAccessibilityEvent(host, event)
-                    }
-                }
-            }
-
         /** Binds overlayView (side fingerprint sensor indicator view) to SideFpsOverlayViewModel */
         fun bind(
             overlayView: View,
@@ -146,7 +184,24 @@
 
                 overlayShowAnimator.start()
 
-                it.accessibilityDelegate = accessibilityDelegate
+                it.setAccessibilityDelegate(
+                    object : View.AccessibilityDelegate() {
+                        override fun dispatchPopulateAccessibilityEvent(
+                            host: View,
+                            event: AccessibilityEvent
+                        ): Boolean {
+                            return if (
+                                event.getEventType() ===
+                                    android.view.accessibility.AccessibilityEvent
+                                        .TYPE_WINDOW_STATE_CHANGED
+                            ) {
+                                true
+                            } else {
+                                super.dispatchPopulateAccessibilityEvent(host, event)
+                            }
+                        }
+                    }
+                )
 
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
                     launch {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 85f221f..168ba11 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -78,11 +78,11 @@
 class PromptViewModel
 @Inject
 constructor(
-    private val displayStateInteractor: DisplayStateInteractor,
+    displayStateInteractor: DisplayStateInteractor,
     private val promptSelectorInteractor: PromptSelectorInteractor,
     @Application private val context: Context,
-    udfpsOverlayInteractor: UdfpsOverlayInteractor,
-    biometricStatusInteractor: BiometricStatusInteractor,
+    private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+    private val biometricStatusInteractor: BiometricStatusInteractor,
     private val udfpsUtils: UdfpsUtils,
     private val iconProvider: IconProvider,
     private val activityTaskManager: ActivityTaskManager,
@@ -135,13 +135,11 @@
             R.dimen.biometric_prompt_landscape_medium_horizontal_padding
         )
 
-    val currentRotation: StateFlow<DisplayRotation> = displayStateInteractor.currentRotation
-
     val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
         udfpsOverlayInteractor.udfpsOverlayParams
 
     private val udfpsSensorBounds: Flow<Rect> =
-        combine(udfpsOverlayParams, currentRotation) { params, rotation ->
+        combine(udfpsOverlayParams, displayStateInteractor.currentRotation) { params, rotation ->
                 val rotatedBounds = Rect(params.sensorBounds)
                 RotationUtils.rotateBounds(
                     rotatedBounds,
@@ -264,7 +262,7 @@
                 _forceLargeSize,
                 promptKind,
                 displayStateInteractor.isLargeScreen,
-                currentRotation,
+                displayStateInteractor.currentRotation,
                 modalities
             ) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
                 when {
@@ -456,7 +454,7 @@
 
     /** Padding for prompt UI elements */
     val promptPadding: Flow<Rect> =
-        combine(size, currentRotation) { size, rotation ->
+        combine(size, displayStateInteractor.currentRotation) { size, rotation ->
             if (size != PromptSize.LARGE) {
                 val navBarInsets = Utils.getNavbarInsets(context)
                 if (rotation == DisplayRotation.ROTATION_90) {
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 7c1984e..c2a4ee3 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
@@ -147,7 +147,8 @@
             _lottieBounds,
             sensorLocation,
             displayRotation,
-        ) { _: Rect?, sensorLocation: SideFpsSensorLocation, _: DisplayRotation ->
+        ) { bounds: Rect?, sensorLocation: SideFpsSensorLocation, displayRotation: DisplayRotation
+            ->
             val topLeft = Point(sensorLocation.left, sensorLocation.top)
 
             defaultOverlayViewParams.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
index 8b5a09b..2c026c0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
@@ -103,7 +103,7 @@
                 prepareToPerformAction()
                 returnToCall()
             },
-            onLongClick = null
+            onLongClick = null,
         )
     }
 
@@ -115,15 +115,15 @@
                 dozeLogger.logEmergencyCall()
                 startEmergencyDialerActivity()
             },
-            // TODO(b/308001302): The long click detector doesn't work properly, investigate.
+            // TODO(b/369767936): The long click detector doesn't work properly, investigate.
             onLongClick = {
                 if (emergencyAffordanceManager.needsEmergencyAffordance()) {
                     prepareToPerformAction()
 
-                    // TODO(b/298026988): Check that !longPressWasDragged before invoking.
+                    // TODO(b/369767936): Check that !longPressWasDragged before invoking.
                     emergencyAffordanceManager.performEmergencyCall()
                 }
-            }
+            },
         )
     }
 
@@ -143,7 +143,7 @@
             applicationContext.startActivityAsUser(
                 this,
                 ActivityOptions.makeCustomAnimation(applicationContext, 0, 0).toBundle(),
-                UserHandle(selectedUserInteractor.getSelectedUserId())
+                UserHandle(selectedUserInteractor.getSelectedUserId()),
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index b5e54d5..fdbc18d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -5,7 +5,6 @@
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.compose.ui.platform.ComposeView
-import androidx.core.view.isGone
 import androidx.lifecycle.Lifecycle
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
 import com.android.systemui.bouncer.ui.composable.BouncerContainer
@@ -13,7 +12,6 @@
 import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
 import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
 import com.android.systemui.lifecycle.viewModel
 import kotlinx.coroutines.awaitCancellation
 
@@ -50,7 +48,6 @@
                             setContent { BouncerContainer(viewModelFactory, dialogFactory) }
                         }
                     )
-                    view.setSnapshotBinding { view.isGone = !viewModel.isVisible }
                     awaitCancellation()
                 } finally {
                     view.removeAllViews()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 873d1b3..4185aed 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -86,6 +86,9 @@
 
             _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
             clearInput()
+            if (authenticationResult == AuthenticationResult.SUCCEEDED) {
+                onSuccessfulAuthentication()
+            }
         }
         awaitCancellation()
     }
@@ -116,6 +119,9 @@
     /** Returns the input entered so far. */
     protected abstract fun getInput(): List<Any>
 
+    /** Invoked after a successful authentication. */
+    protected open fun onSuccessfulAuthentication() = Unit
+
     /** Perform authentication result haptics */
     private fun performAuthenticationHapticFeedback(result: AuthenticationResult) {
         if (result == AuthenticationResult.SKIPPED) return
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
index c60f932..5a4f8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
@@ -16,17 +16,15 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
-import androidx.compose.runtime.getValue
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
@@ -39,11 +37,6 @@
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
 ) : ExclusiveActivatable() {
 
-    private val hydrator = Hydrator("BouncerContainerViewModel")
-
-    val isVisible: Boolean by
-        hydrator.hydratedStateOf(traceName = "isVisible", source = legacyInteractor.isShowing)
-
     override suspend fun onActivated(): Nothing {
         coroutineScope {
             launch {
@@ -74,8 +67,7 @@
                     legacyInteractor.hide()
                 }
             }
-
-            hydrator.activate()
+            awaitCancellation()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index 2493cf1..1427d78 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -81,50 +81,59 @@
     val selectedUserId: StateFlow<Int> = _selectedUserId.asStateFlow()
 
     private val requests = Channel<Request>(Channel.BUFFERED)
+    private var wasSuccessfullyAuthenticated = false
 
     override suspend fun onActivated(): Nothing {
-        coroutineScope {
-            launch { super.onActivated() }
-            launch {
-                requests.receiveAsFlow().collect { request ->
-                    when (request) {
-                        is OnImeSwitcherButtonClicked -> {
-                            inputMethodInteractor.showInputMethodPicker(
-                                displayId = request.displayId,
-                                showAuxiliarySubtypes = false,
-                            )
-                        }
-                        is OnImeDismissed -> {
-                            interactor.onImeHiddenByUser()
+        try {
+            coroutineScope {
+                launch { super.onActivated() }
+                launch {
+                    requests.receiveAsFlow().collect { request ->
+                        when (request) {
+                            is OnImeSwitcherButtonClicked -> {
+                                inputMethodInteractor.showInputMethodPicker(
+                                    displayId = request.displayId,
+                                    showAuxiliarySubtypes = false,
+                                )
+                            }
+                            is OnImeDismissed -> {
+                                interactor.onImeHiddenByUser()
+                            }
                         }
                     }
                 }
+                launch {
+                    combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus ->
+                            hasInput && !hasFocus && !wasSuccessfullyAuthenticated
+                        }
+                        .collect { _isTextFieldFocusRequested.value = it }
+                }
+                launch {
+                    selectedUserInteractor.selectedUser.collect { _selectedUserId.value = it }
+                }
+                launch {
+                    // Re-fetch the currently-enabled IMEs whenever the selected user changes, and
+                    // whenever
+                    // the UI subscribes to the `isImeSwitcherButtonVisible` flow.
+                    combine(
+                            // InputMethodManagerService sometimes takes
+                            // some time to update its internal state when the
+                            // selected user changes.
+                            // As a workaround, delay fetching the IME info.
+                            selectedUserInteractor.selectedUser.onEach {
+                                delay(DELAY_TO_FETCH_IMES)
+                            },
+                            _isImeSwitcherButtonVisible.onSubscriberAdded(),
+                        ) { selectedUserId, _ ->
+                            inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
+                        }
+                        .collect { _isImeSwitcherButtonVisible.value = it }
+                }
+                awaitCancellation()
             }
-            launch {
-                combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus ->
-                        hasInput && !hasFocus
-                    }
-                    .collect { _isTextFieldFocusRequested.value = it }
-            }
-            launch { selectedUserInteractor.selectedUser.collect { _selectedUserId.value = it } }
-            launch {
-                // Re-fetch the currently-enabled IMEs whenever the selected user changes, and
-                // whenever
-                // the UI subscribes to the `isImeSwitcherButtonVisible` flow.
-                combine(
-                        // InputMethodManagerService sometimes takes some time to update its
-                        // internal
-                        // state when the selected user changes. As a workaround, delay fetching the
-                        // IME
-                        // info.
-                        selectedUserInteractor.selectedUser.onEach { delay(DELAY_TO_FETCH_IMES) },
-                        _isImeSwitcherButtonVisible.onSubscriberAdded()
-                    ) { selectedUserId, _ ->
-                        inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
-                    }
-                    .collect { _isImeSwitcherButtonVisible.value = it }
-            }
-            awaitCancellation()
+        } finally {
+            // reset whenever the view model is "deactivated"
+            wasSuccessfullyAuthenticated = false
         }
     }
 
@@ -141,6 +150,10 @@
         return _password.value.toCharArray().toList()
     }
 
+    override fun onSuccessfulAuthentication() {
+        wasSuccessfullyAuthenticated = true
+    }
+
     /** Notifies that the user has changed the password input. */
     fun onPasswordInputChanged(newPassword: String) {
         if (newPassword.isNotEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
index 12597a7..99c026c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
@@ -24,6 +24,7 @@
 import android.util.Log
 import android.util.Size
 import android.view.textclassifier.TextLinks
+import com.android.systemui.Flags.clipboardUseDescriptionMimetype
 import com.android.systemui.res.R
 import java.io.IOException
 
@@ -70,11 +71,11 @@
             context: Context,
             utils: ClipboardOverlayUtils,
             clipData: ClipData,
-            source: String
+            source: String,
         ): ClipboardModel {
             val sensitive = clipData.description?.extras?.getBoolean(EXTRA_IS_SENSITIVE) ?: false
             val item = clipData.getItemAt(0)!!
-            val type = getType(context, item)
+            val type = getType(context, item, clipData.description.getMimeType(0))
             val remote = utils.isRemoteCopy(context, clipData, source)
             return ClipboardModel(
                 clipData,
@@ -84,18 +85,26 @@
                 item.textLinks,
                 item.uri,
                 sensitive,
-                remote
+                remote,
             )
         }
 
-        private fun getType(context: Context, item: ClipData.Item): Type {
+        private fun getType(context: Context, item: ClipData.Item, mimeType: String): Type {
             return if (!TextUtils.isEmpty(item.text)) {
                 Type.TEXT
             } else if (item.uri != null) {
-                if (context.contentResolver.getType(item.uri)?.startsWith("image") == true) {
-                    Type.IMAGE
+                if (clipboardUseDescriptionMimetype()) {
+                    if (mimeType.startsWith("image")) {
+                        Type.IMAGE
+                    } else {
+                        Type.URI
+                    }
                 } else {
-                    Type.URI
+                    if (context.contentResolver.getType(item.uri)?.startsWith("image") == true) {
+                        Type.IMAGE
+                    } else {
+                        Type.URI
+                    }
                 }
             } else {
                 Type.OTHER
@@ -107,6 +116,6 @@
         TEXT,
         IMAGE,
         URI,
-        OTHER
+        OTHER,
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 04393fe..1bd541e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -65,7 +65,9 @@
                 keyguardTransitionInteractor
                     .transitionValue(Scenes.Communal, KeyguardState.GLANCEABLE_HUB)
                     .map { it == 1f },
-                not(keyguardInteractor.isDreaming),
+                // Use isDreamingAny because isDreaming is false in doze and doesn't change again
+                // when the screen turns on, which causes the dream to not start underneath the hub.
+                not(keyguardInteractor.isDreamingAny),
                 // TODO(b/362830856): Remove this workaround.
                 keyguardInteractor.isKeyguardShowing,
                 not(communalSceneInteractor.isLaunchingWidget),
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
index f77dd58..f0f7ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
@@ -82,19 +82,26 @@
             }
 
         _timers.value =
-            timerTargets.map { (stableId, target) ->
-                CommunalSmartspaceTimer(
-                    // The view layer should have the instance based smartspaceTargetId instead of
-                    // stable id, so that when a new instance of the timer is created, for example,
-                    // when it is paused, the view should re-render its remote views.
-                    smartspaceTargetId =
-                        if (communalTimerFlickerFix()) stableId else target.smartspaceTargetId,
-                    createdTimestampMillis = targetCreationTimes[stableId]!!,
-                    remoteViews = target.remoteViews!!,
-                )
-            }
-
-        logger.d({ "Smartspace timers updated: $str1" }) { str1 = _timers.value.toString() }
+            timerTargets
+                .map { (stableId, target) ->
+                    CommunalSmartspaceTimer(
+                        // The view layer should have the instance based smartspaceTargetId instead
+                        // of stable id, so that when a new instance of the timer is created, for
+                        // example, when it is paused, the view should re-render its remote views.
+                        smartspaceTargetId =
+                            if (communalTimerFlickerFix()) stableId else target.smartspaceTargetId,
+                        createdTimestampMillis = targetCreationTimes[stableId]!!,
+                        remoteViews = target.remoteViews!!,
+                    )
+                }
+                .also { newVal ->
+                    // Only log when value changes to avoid filling up the buffer.
+                    if (newVal != _timers.value) {
+                        logger.d({ "Smartspace timers updated: $str1" }) {
+                            str1 = newVal.toString()
+                        }
+                    }
+                }
     }
 
     override fun startListening() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index c7538bb4..905eda1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -87,7 +87,9 @@
      */
     private val nextKeyguardStateInternal =
         combine(
-                keyguardInteractor.isAbleToDream,
+                // Don't use delayed dreaming signal as otherwise we might go to occluded or lock
+                // screen when closing hub if dream just started under the hub.
+                keyguardInteractor.isDreamingWithOverlay,
                 keyguardInteractor.isKeyguardOccluded,
                 keyguardInteractor.isKeyguardGoingAway,
                 keyguardInteractor.isKeyguardShowing,
@@ -156,7 +158,7 @@
 
     private suspend fun handleIdle(
         prevTransition: ObservableTransitionState,
-        idle: ObservableTransitionState.Idle
+        idle: ObservableTransitionState.Idle,
     ) {
         if (
             prevTransition is ObservableTransitionState.Transition &&
@@ -186,7 +188,7 @@
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
             1f,
-            TransitionState.FINISHED
+            TransitionState.FINISHED,
         )
         resetTransitionData()
     }
@@ -204,7 +206,7 @@
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
             1f,
-            TransitionState.FINISHED
+            TransitionState.FINISHED,
         )
         resetTransitionData()
     }
@@ -217,7 +219,7 @@
 
     private suspend fun handleTransition(
         prevTransition: ObservableTransitionState,
-        transition: ObservableTransitionState.Transition
+        transition: ObservableTransitionState.Transition,
     ) {
         if (
             prevTransition.isTransitioning(from = transition.fromContent, to = transition.toContent)
@@ -295,7 +297,7 @@
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
             progress.coerceIn(0f, 1f),
-            TransitionState.RUNNING
+            TransitionState.RUNNING,
         )
     }
 }
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 7453368..f7cd2ab 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
@@ -16,10 +16,13 @@
 
 package com.android.systemui.communal.domain.interactor
 
+import android.annotation.SuppressLint
 import android.app.ActivityManager
+import android.app.DreamManager
 import com.android.systemui.common.usagestats.domain.UsageStatsInteractor
 import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.log.LogBuffer
@@ -34,10 +37,12 @@
 import javax.inject.Inject
 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
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withTimeout
 
@@ -56,6 +61,8 @@
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val taskStackChangeListeners: TaskStackChangeListeners,
     private val usageStatsInteractor: UsageStatsInteractor,
+    private val dreamManager: DreamManager,
+    @Background private val bgScope: CoroutineScope,
     @CommunalLog logBuffer: LogBuffer,
 ) {
     private companion object {
@@ -127,13 +134,21 @@
      * Checks if an activity starts while on the glanceable hub and dismisses the keyguard if it
      * does. This can detect activities started due to broadcast trampolines from widgets.
      */
+    @SuppressLint("MissingPermission")
     suspend fun waitForActivityStartAndDismissKeyguard() {
         if (waitForActivityStartWhileOnHub()) {
             logger.d("Detected trampoline, requesting unlock")
             activityStarter.dismissKeyguardThenExecute(
-                /* action= */ { false },
+                /* action= */ {
+                    // Kill the dream when launching the trampoline activity. Right now the exit
+                    // animation stalls when tapping the battery widget, and the dream remains
+                    // visible until the transition hits some timeouts and gets cancelled.
+                    // TODO(b/362841648): remove once exit animation is fixed.
+                    bgScope.launch { dreamManager.stopDream() }
+                    false
+                },
                 /* cancel= */ null,
-                /* afterKeyguardGone= */ false
+                /* afterKeyguardGone= */ false,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
index ec03227..cafa74f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
 import com.android.systemui.communal.widgets.WidgetInteractionHandler
 import com.android.systemui.dagger.qualifiers.UiBackground
+import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 
@@ -34,6 +35,7 @@
 @Inject
 constructor(
     @UiBackground private val uiBgContext: CoroutineContext,
+    @UiBackground private val uiBgExecutor: Executor,
     private val appWidgetHost: CommunalAppWidgetHost,
     private val interactionHandler: WidgetInteractionHandler,
     private val listenerFactory: AppWidgetHostListenerDelegate.Factory,
@@ -44,8 +46,11 @@
         size: SizeF,
     ): CommunalAppWidgetHostView =
         withContext("$TAG#createWidget", uiBgContext) {
-            val view = CommunalAppWidgetHostView(context, interactionHandler)
-            view.setAppWidget(model.appWidgetId, model.providerInfo)
+            val view =
+                CommunalAppWidgetHostView(context, interactionHandler).apply {
+                    setExecutor(uiBgExecutor)
+                    setAppWidget(model.appWidgetId, model.providerInfo)
+                }
             // Instead of setting the view as the listener directly, we wrap the view in a delegate
             // which ensures the callbacks always get called on the main thread.
             appWidgetHost.setListener(model.appWidgetId, listenerFactory.create(view))
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 79f4568..5c075c2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.phone.DozeServiceHost;
 import com.android.systemui.statusbar.phone.HeadsUpModule;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule;
 import com.android.systemui.statusbar.policy.AospPolicyModule;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -136,6 +137,7 @@
         RotationLockModule.class,
         RotationLockNewModule.class,
         ScreenDecorationsModule.class,
+        StatusBarPhoneModule.class,
         SystemActionsModule.class,
         ShadeModule.class,
         StartCentralSurfacesModule.class,
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 28db3b8..f90f02a 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
@@ -70,7 +70,14 @@
 ) {
     private val keyguardOccludedByApp: Flow<Boolean> =
         if (KeyguardWmStateRefactor.isEnabled) {
-            keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.OCCLUDED }
+            combine(
+                    keyguardTransitionInteractor.currentKeyguardState,
+                    communalSceneInteractor.isIdleOnCommunal,
+                    ::Pair,
+                )
+                .map { (currentState, isIdleOnCommunal) ->
+                    currentState == KeyguardState.OCCLUDED && !isIdleOnCommunal
+                }
         } else {
             combine(
                     keyguardInteractor.isKeyguardOccluded,
@@ -120,7 +127,7 @@
             // On fingerprint success when the screen is on and not dreaming, go to the home screen
             fingerprintUnlockSuccessEvents
                 .sample(
-                    combine(powerInteractor.isInteractive, keyguardInteractor.isDreaming, ::Pair),
+                    combine(powerInteractor.isInteractive, keyguardInteractor.isDreaming, ::Pair)
                 )
                 .collect { (interactive, dreaming) ->
                     if (interactive && !dreaming) {
@@ -148,7 +155,7 @@
                         }
                     },
                     /* cancel= */ null,
-                    /* afterKeyguardGone */ false
+                    /* afterKeyguardGone */ false,
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index a327e4a..e68aba5 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -45,6 +45,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -73,6 +74,14 @@
     /** Whether the default display is currently off. */
     val defaultDisplayOff: Flow<Boolean>
 
+    /**
+     * Given a display ID int, return the corresponding Display object, or null if none exist.
+     *
+     * This method is guaranteed to not result in any binder call.
+     */
+    suspend fun getDisplay(displayId: Int): Display? =
+        displays.first().firstOrNull { it.displayId == displayId }
+
     /** Represents a connected display that has not been enabled yet. */
     interface PendingDisplay {
         /** Id of the pending display. */
@@ -135,7 +144,9 @@
         allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
 
     override val displayAdditionEvent: Flow<Display?> =
-        allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { getDisplay(it.displayId) }
+        allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map {
+            getDisplayFromDisplayManager(it.displayId)
+        }
 
     // This is necessary because there might be multiple displays, and we could
     // have missed events for those added before this process or flow started.
@@ -160,7 +171,8 @@
             .debugLog("enabledDisplayIds")
 
     private val defaultDisplay by lazy {
-        getDisplay(Display.DEFAULT_DISPLAY) ?: error("Unable to get default display.")
+        getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
+            ?: error("Unable to get default display.")
     }
 
     /**
@@ -170,7 +182,7 @@
      */
     private val enabledDisplays: Flow<Set<Display>> =
         enabledDisplayIds
-            .mapElementsLazily { displayId -> getDisplay(displayId) }
+            .mapElementsLazily { displayId -> getDisplayFromDisplayManager(displayId) }
             .onEach {
                 if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
             }
@@ -269,7 +281,7 @@
     private fun getDisplayType(displayId: Int): Int? =
         traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
 
-    private fun getDisplay(displayId: Int): Display? =
+    private fun getDisplayFromDisplayManager(displayId: Int): Display? =
         traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
 
     /**
@@ -354,8 +366,8 @@
      * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into
      * account the diff between each root flow emission.
      *
-     * This is needed to minimize the number of [getDisplay] in this class. Note that if the
-     * [createValue] returns a null element, it will not be added in the output set.
+     * This is needed to minimize the number of [getDisplayFromDisplayManager] in this class. Note
+     * that if the [createValue] returns a null element, it will not be added in the output set.
      */
     private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
         data class State<T, V>(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
index 77c54ec..3992c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
@@ -39,6 +39,7 @@
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 
 class HomeControlsDreamService
 @Inject
@@ -53,7 +54,7 @@
 ) : DreamService() {
 
     private val serviceJob = SupervisorJob()
-    private val serviceScope = CoroutineScope(bgDispatcher + serviceJob)
+    private val serviceScope = CoroutineScope(bgDispatcher + serviceJob + createCoroutineTracingContext("HomeControlsDreamService"))
     private val logger = DreamLogger(logBuffer, TAG)
     private lateinit var taskFragmentComponent: TaskFragmentComponent
     private val wakeLock: WakeLock by lazy {
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 7e2c9f8..4caf95b 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.education.dagger
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags
 import com.android.systemui.contextualeducation.GestureType
@@ -56,7 +57,7 @@
         fun provideEduDataStoreScope(
             @Background bgDispatcher: CoroutineDispatcher
         ): CoroutineScope {
-            return CoroutineScope(bgDispatcher + SupervisorJob())
+            return CoroutineScope(bgDispatcher + SupervisorJob() + createCoroutineTracingContext("EduDataStoreScope"))
         }
 
         @EduClock
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 0b775ab..47f0ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -34,10 +34,14 @@
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment
 import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
 import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
 import javax.inject.Inject
@@ -55,7 +59,9 @@
         // Internal notification frontend dependencies
         NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
         PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
-        NotificationMinimalismPrototype.token dependsOn NotificationThrottleHun.token
+        NotificationMinimalism.token dependsOn NotificationThrottleHun.token
+        ModesEmptyShadeFix.token dependsOn FooterViewRefactor.token
+        ModesEmptyShadeFix.token dependsOn modesUi
 
         // SceneContainer dependencies
         SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
@@ -69,6 +75,8 @@
         // Status bar chip dependencies
         statusBarCallChipNotificationIconToken dependsOn statusBarUseReposForCallChipToken
         statusBarCallChipNotificationIconToken dependsOn statusBarScreenSharingChipsToken
+
+        StatusBarConnectedDisplays.token dependsOn StatusBarSimpleFragment.token
     }
 
     private inline val politeNotifications
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 493afde..aa1873c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -2298,9 +2298,11 @@
                     }
 
                     @Override
-                    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+                    public boolean onScroll(@Nullable MotionEvent e1, MotionEvent e2,
+                            float distanceX,
                             float distanceY) {
                         if (distanceY < 0 && distanceY > distanceX
+                                && e1 != null
                                 && e1.getY() <= mStatusBarWindowController.getStatusBarHeight()) {
                             // Downwards scroll from top
                             openShadeAndDismiss();
@@ -2310,9 +2312,11 @@
                     }
 
                     @Override
-                    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                    public boolean onFling(@Nullable MotionEvent e1, MotionEvent e2,
+                            float velocityX,
                             float velocityY) {
                         if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
+                                && e1 != null
                                 && e1.getY() <= mStatusBarWindowController.getStatusBarHeight()) {
                             // Downwards fling from top
                             openShadeAndDismiss();
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
new file mode 100644
index 0000000..62ab18b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/grid/ui/compose/SpannedGrids.kt
@@ -0,0 +1,321 @@
+/*
+ * 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.grid.ui.compose
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import kotlin.math.max
+
+/**
+ * Horizontal (non lazy) grid that supports [spans] for its elements.
+ *
+ * The elements will be laid down vertically first, and then by columns. So assuming LTR layout, it
+ * will be (for a span list `[2, 1, 2, 1, 1, 1, 1, 1]` and 4 rows):
+ * ```
+ * 0  2  5
+ * 0  2  6
+ * 1  3  7
+ *    4
+ * ```
+ *
+ * where repeated numbers show larger span. If an element doesn't fit in a column due to its span,
+ * it will start a new column.
+ *
+ * Elements in [spans] must be in the interval `[1, rows]` ([rows] > 0), and the composables are
+ * associated with the corresponding span based on their index.
+ *
+ * Due to the fact that elements are seen as a linear list that's laid out in a grid, the semantics
+ * represent the collection as a list of elements.
+ */
+@Composable
+fun HorizontalSpannedGrid(
+    rows: Int,
+    columnSpacing: Dp,
+    rowSpacing: Dp,
+    spans: List<Int>,
+    modifier: Modifier = Modifier,
+    composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
+) {
+    SpannedGrid(
+        primarySpaces = rows,
+        crossAxisSpacing = rowSpacing,
+        mainAxisSpacing = columnSpacing,
+        spans = spans,
+        isVertical = false,
+        modifier = modifier,
+        composables = composables,
+    )
+}
+
+/**
+ * Horizontal (non lazy) grid that supports [spans] for its elements.
+ *
+ * The elements will be laid down horizontally first, and then by rows. So assuming LTR layout, it
+ * will be (for a span list `[2, 1, 2, 1, 1, 1, 1, 1]` and 4 columns):
+ * ```
+ * 0  0  1
+ * 2  2  3  4
+ * 5  6  7
+ * ```
+ *
+ * where repeated numbers show larger span. If an element doesn't fit in a row due to its span, it
+ * will start a new row.
+ *
+ * Elements in [spans] must be in the interval `[1, columns]` ([columns] > 0), and the composables
+ * are associated with the corresponding span based on their index.
+ *
+ * Due to the fact that elements are seen as a linear list that's laid out in a grid, the semantics
+ * represent the collection as a list of elements.
+ */
+@Composable
+fun VerticalSpannedGrid(
+    columns: Int,
+    columnSpacing: Dp,
+    rowSpacing: Dp,
+    spans: List<Int>,
+    modifier: Modifier = Modifier,
+    composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
+) {
+    SpannedGrid(
+        primarySpaces = columns,
+        crossAxisSpacing = columnSpacing,
+        mainAxisSpacing = rowSpacing,
+        spans = spans,
+        isVertical = true,
+        modifier = modifier,
+        composables = composables,
+    )
+}
+
+@Composable
+private fun SpannedGrid(
+    primarySpaces: Int,
+    crossAxisSpacing: Dp,
+    mainAxisSpacing: Dp,
+    spans: List<Int>,
+    isVertical: Boolean,
+    modifier: Modifier = Modifier,
+    composables: @Composable BoxScope.(spanIndex: Int) -> Unit,
+) {
+    val crossAxisArrangement = Arrangement.spacedBy(crossAxisSpacing)
+    spans.forEachIndexed { index, span ->
+        check(span in 1..primarySpaces) {
+            "Span out of bounds. Span at index $index has value of $span which is outside of the " +
+                "expected rance of [1, $primarySpaces]"
+        }
+    }
+
+    if (isVertical) {
+        check(crossAxisSpacing >= 0.dp) { "Negative columnSpacing $crossAxisSpacing" }
+        check(mainAxisSpacing >= 0.dp) { "Negative rowSpacing $mainAxisSpacing" }
+    } else {
+        check(mainAxisSpacing >= 0.dp) { "Negative columnSpacing $mainAxisSpacing" }
+        check(crossAxisSpacing >= 0.dp) { "Negative rowSpacing $crossAxisSpacing" }
+    }
+
+    val totalMainAxisGroups: Int =
+        remember(primarySpaces, spans) {
+            var currentAccumulated = 0
+            var groups = 1
+            spans.forEach { span ->
+                if (currentAccumulated + span <= primarySpaces) {
+                    currentAccumulated += span
+                } else {
+                    groups += 1
+                    currentAccumulated = span
+                }
+            }
+            groups
+        }
+
+    val slotPositionsAndSizesCache = remember {
+        object {
+            var sizes = IntArray(0)
+            var positions = IntArray(0)
+        }
+    }
+
+    Layout(
+        {
+            (0 until spans.size).map { spanIndex ->
+                Box(
+                    Modifier.semantics {
+                        collectionItemInfo =
+                            if (isVertical) {
+                                CollectionItemInfo(spanIndex, 1, 0, 1)
+                            } else {
+                                CollectionItemInfo(0, 1, spanIndex, 1)
+                            }
+                    }
+                ) {
+                    composables(spanIndex)
+                }
+            }
+        },
+        modifier.semantics { collectionInfo = CollectionInfo(spans.size, 1) },
+    ) { measurables, constraints ->
+        check(measurables.size == spans.size)
+        val crossAxisSize = if (isVertical) constraints.maxWidth else constraints.maxHeight
+        check(crossAxisSize != Constraints.Infinity) { "Width must be constrained" }
+        if (slotPositionsAndSizesCache.sizes.size != primarySpaces) {
+            slotPositionsAndSizesCache.sizes = IntArray(primarySpaces)
+            slotPositionsAndSizesCache.positions = IntArray(primarySpaces)
+        }
+        calculateCellsCrossAxisSize(
+            crossAxisSize,
+            primarySpaces,
+            crossAxisSpacing.roundToPx(),
+            slotPositionsAndSizesCache.sizes,
+        )
+        val cellSizesInCrossAxis = slotPositionsAndSizesCache.sizes
+
+        // with is needed because of the double receiver (Density, Arrangement).
+        with(crossAxisArrangement) {
+            arrange(
+                crossAxisSize,
+                slotPositionsAndSizesCache.sizes,
+                LayoutDirection.Ltr,
+                slotPositionsAndSizesCache.positions,
+            )
+        }
+        val startPositions = slotPositionsAndSizesCache.positions
+
+        val mainAxisSpacingPx = mainAxisSpacing.roundToPx()
+        val mainAxisTotalGaps = (totalMainAxisGroups - 1) * mainAxisSpacingPx
+        val mainAxisSize = if (isVertical) constraints.maxHeight else constraints.maxWidth
+        val mainAxisElementConstraint =
+            if (mainAxisSize == Constraints.Infinity) {
+                Constraints.Infinity
+            } else {
+                max(0, (mainAxisSize - mainAxisTotalGaps) / totalMainAxisGroups)
+            }
+
+        val mainAxisSizes = IntArray(totalMainAxisGroups) { 0 }
+
+        var currentSlot = 0
+        var mainAxisGroup = 0
+        val placeables =
+            measurables.mapIndexed { index, measurable ->
+                val span = spans[index]
+                if (currentSlot + span > primarySpaces) {
+                    currentSlot = 0
+                    mainAxisGroup += 1
+                }
+                val crossAxisConstraint =
+                    calculateWidth(cellSizesInCrossAxis, startPositions, currentSlot, span)
+                PlaceResult(
+                        measurable.measure(
+                            makeConstraint(
+                                isVertical,
+                                mainAxisElementConstraint,
+                                crossAxisConstraint,
+                            )
+                        ),
+                        currentSlot,
+                        mainAxisGroup,
+                    )
+                    .also {
+                        currentSlot += span
+                        mainAxisSizes[mainAxisGroup] =
+                            max(
+                                mainAxisSizes[mainAxisGroup],
+                                if (isVertical) it.placeable.height else it.placeable.width,
+                            )
+                    }
+            }
+
+        val mainAxisTotalSize = mainAxisTotalGaps + mainAxisSizes.sum()
+        val mainAxisStartingPoints =
+            mainAxisSizes.runningFold(0) { acc, value -> acc + value + mainAxisSpacingPx }
+        val height = if (isVertical) mainAxisTotalSize else crossAxisSize
+        val width = if (isVertical) crossAxisSize else mainAxisTotalSize
+
+        layout(width, height) {
+            placeables.forEach { (placeable, slot, mainAxisGroup) ->
+                val x =
+                    if (isVertical) {
+                        startPositions[slot]
+                    } else {
+                        mainAxisStartingPoints[mainAxisGroup]
+                    }
+                val y =
+                    if (isVertical) {
+                        mainAxisStartingPoints[mainAxisGroup]
+                    } else {
+                        startPositions[slot]
+                    }
+                placeable.placeRelative(x, y)
+            }
+        }
+    }
+}
+
+fun makeConstraint(isVertical: Boolean, mainAxisSize: Int, crossAxisSize: Int): Constraints {
+    return if (isVertical) {
+        Constraints(maxHeight = mainAxisSize, minWidth = crossAxisSize, maxWidth = crossAxisSize)
+    } else {
+        Constraints(maxWidth = mainAxisSize, minHeight = crossAxisSize, maxHeight = crossAxisSize)
+    }
+}
+
+private fun calculateWidth(sizes: IntArray, positions: IntArray, startSlot: Int, span: Int): Int {
+    val crossAxisSize =
+        if (span == 1) {
+                sizes[startSlot]
+            } else {
+                val endSlot = startSlot + span - 1
+                positions[endSlot] + sizes[endSlot] - positions[startSlot]
+            }
+            .coerceAtLeast(0)
+    return crossAxisSize
+}
+
+private fun calculateCellsCrossAxisSize(
+    gridSize: Int,
+    slotCount: Int,
+    spacingPx: Int,
+    outArray: IntArray,
+) {
+    check(outArray.size == slotCount)
+    val gridSizeWithoutSpacing = gridSize - spacingPx * (slotCount - 1)
+    val slotSize = gridSizeWithoutSpacing / slotCount
+    val remainingPixels = gridSizeWithoutSpacing % slotCount
+    outArray.indices.forEach { index ->
+        outArray[index] = slotSize + if (index < remainingPixels) 1 else 0
+    }
+}
+
+private data class PlaceResult(
+    val placeable: Placeable,
+    val slotIndex: Int,
+    val mainAxisGroup: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
index b271356..f32c94b 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -22,6 +22,7 @@
 import androidx.annotation.RawRes
 import androidx.annotation.StringRes
 import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.EnterTransition
 import androidx.compose.animation.ExitTransition
 import androidx.compose.animation.core.LinearEasing
@@ -40,6 +41,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawingPadding
 import androidx.compose.foundation.layout.width
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
@@ -67,21 +69,22 @@
 enum class TutorialActionState {
     NOT_STARTED,
     IN_PROGRESS,
-    FINISHED
+    FINISHED,
 }
 
 @Composable
 fun ActionTutorialContent(
     actionState: TutorialActionState,
     onDoneButtonClicked: () -> Unit,
-    config: TutorialScreenConfig
+    config: TutorialScreenConfig,
 ) {
     Column(
         verticalArrangement = Arrangement.Center,
         modifier =
             Modifier.fillMaxSize()
                 .background(config.colors.background)
-                .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
+                .safeDrawingPadding()
+                .padding(start = 48.dp, top = 100.dp, end = 48.dp, bottom = 8.dp),
     ) {
         Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
             TutorialDescription(
@@ -92,16 +95,18 @@
                 bodyTextId =
                     if (actionState == FINISHED) config.strings.bodySuccessResId
                     else config.strings.bodyResId,
-                modifier = Modifier.weight(1f)
+                modifier = Modifier.weight(1f),
             )
             Spacer(modifier = Modifier.width(76.dp))
             TutorialAnimation(
                 actionState,
                 config,
-                modifier = Modifier.weight(1f).padding(top = 8.dp)
+                modifier = Modifier.weight(1f).padding(top = 8.dp),
             )
         }
-        DoneButton(onDoneButtonClicked = onDoneButtonClicked)
+        AnimatedVisibility(visible = actionState == FINISHED, enter = fadeIn()) {
+            DoneButton(onDoneButtonClicked = onDoneButtonClicked)
+        }
     }
 }
 
@@ -110,19 +115,19 @@
     @StringRes titleTextId: Int,
     titleColor: Color,
     @StringRes bodyTextId: Int,
-    modifier: Modifier = Modifier
+    modifier: Modifier = Modifier,
 ) {
     Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
         Text(
             text = stringResource(id = titleTextId),
             style = MaterialTheme.typography.displayLarge,
-            color = titleColor
+            color = titleColor,
         )
         Spacer(modifier = Modifier.height(16.dp))
         Text(
             text = stringResource(id = bodyTextId),
             style = MaterialTheme.typography.bodyLarge,
-            color = Color.White
+            color = Color.White,
         )
     }
 }
@@ -131,7 +136,7 @@
 fun TutorialAnimation(
     actionState: TutorialActionState,
     config: TutorialScreenConfig,
-    modifier: Modifier = Modifier
+    modifier: Modifier = Modifier,
 ) {
     Box(modifier = modifier.fillMaxWidth()) {
         AnimatedContent(
@@ -152,18 +157,18 @@
                     // state which shares initial animation frame with both FINISHED and NOT_STARTED
                     EnterTransition.None togetherWith ExitTransition.None
                 }
-            }
+            },
         ) { state ->
             when (state) {
                 NOT_STARTED ->
                     EducationAnimation(
                         config.animations.educationResId,
-                        config.colors.animationColors
+                        config.colors.animationColors,
                     )
                 IN_PROGRESS ->
                     FrozenSuccessAnimation(
                         config.animations.successResId,
-                        config.colors.animationColors
+                        config.colors.animationColors,
                     )
                 FINISHED ->
                     SuccessAnimation(config.animations.successResId, config.colors.animationColors)
@@ -175,7 +180,7 @@
 @Composable
 private fun FrozenSuccessAnimation(
     @RawRes successAnimationId: Int,
-    animationProperties: LottieDynamicProperties
+    animationProperties: LottieDynamicProperties,
 ) {
     val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
     LottieAnimation(
@@ -188,7 +193,7 @@
 @Composable
 private fun EducationAnimation(
     @RawRes educationAnimationId: Int,
-    animationProperties: LottieDynamicProperties
+    animationProperties: LottieDynamicProperties,
 ) {
     val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(educationAnimationId))
     val progress by
@@ -203,7 +208,7 @@
 @Composable
 private fun SuccessAnimation(
     @RawRes successAnimationId: Int,
-    animationProperties: LottieDynamicProperties
+    animationProperties: LottieDynamicProperties,
 ) {
     val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId))
     val progress by animateLottieCompositionAsState(composition, iterations = 1)
@@ -217,13 +222,13 @@
 @Composable
 fun rememberColorFilterProperty(
     layerName: String,
-    color: Color
+    color: Color,
 ): LottieDynamicProperty<ColorFilter> {
     return rememberLottieDynamicProperty(
         LottieProperty.COLOR_FILTER,
         value = PorterDuffColorFilter(color.toArgb(), PorterDuff.Mode.SRC_ATOP),
         // "**" below means match zero or more layers, so ** layerName ** means find layer with that
         // name at any depth
-        keyPath = arrayOf("**", layerName, "**")
+        keyPath = arrayOf("**", layerName, "**"),
     )
 }
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 4bf552e..5e05dab 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
@@ -36,6 +36,7 @@
 import androidx.compose.foundation.layout.FlowRowScope
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -71,8 +72,6 @@
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBarDefaults
-import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
-import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
@@ -116,7 +115,6 @@
 import androidx.compose.ui.util.fastForEachIndexed
 import androidx.compose.ui.zIndex
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
-import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
@@ -188,10 +186,7 @@
     }
 }
 
-@Composable
-private fun shouldUseSinglePane() =
-    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact ||
-        LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
+@Composable private fun shouldUseSinglePane() = hasCompactWindowSize()
 
 @Composable
 private fun ShortcutHelperSinglePane(
@@ -408,7 +403,7 @@
     SubCategoryTitle(subCategory.label)
     subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
         if (index > 0) {
-            HorizontalDivider()
+            HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainerHigh)
         }
         ShortcutView(Modifier.padding(vertical = 24.dp), searchQuery, shortcut)
     }
@@ -425,7 +420,7 @@
     onKeyboardSettingsClicked: () -> Unit,
 ) {
     val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
-    Column(modifier = modifier.fillMaxSize().padding(start = 24.dp, end = 24.dp, top = 26.dp)) {
+    Column(modifier = modifier.fillMaxSize().padding(horizontal = 24.dp)) {
         TitleBar()
         Spacer(modifier = Modifier.height(12.dp))
         Row(Modifier.fillMaxWidth()) {
@@ -487,7 +482,7 @@
             Spacer(Modifier.height(8.dp))
             subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
                 if (index > 0) {
-                    HorizontalDivider()
+                    HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainerHigh)
                 }
                 ShortcutView(Modifier.padding(vertical = 16.dp), searchQuery, shortcut)
             }
@@ -801,6 +796,8 @@
                 style = MaterialTheme.typography.headlineSmall,
             )
         },
+        windowInsets = WindowInsets(top = 0.dp, bottom = 0.dp, left = 0.dp, right = 0.dp),
+        expandedHeight = 64.dp,
     )
 }
 
@@ -835,6 +832,7 @@
         onSearch = {},
         leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
         placeholder = { Text(text = stringResource(R.string.shortcut_helper_search_placeholder)) },
+        windowInsets = WindowInsets(top = 0.dp, bottom = 0.dp, left = 0.dp, right = 0.dp),
         content = {},
     )
 }
@@ -847,9 +845,7 @@
         shape = RoundedCornerShape(24.dp),
         color = Color.Transparent,
         modifier =
-            Modifier.semantics { role = Role.Button }
-                .fillMaxWidth()
-                .padding(horizontal = 12.dp),
+            Modifier.semantics { role = Role.Button }.fillMaxWidth().padding(horizontal = 12.dp),
         interactionSource = interactionSource,
         interactionsConfig =
             InteractionsConfig(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperUtils.kt
new file mode 100644
index 0000000..1f0d696
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperUtils.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.keyboard.shortcut.ui.composable
+
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
+import androidx.compose.runtime.Composable
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
+
+/**
+ * returns true if either size of the window is compact. This represents majority of phone windows
+ * portrait
+ */
+@Composable
+fun hasCompactWindowSize() =
+    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact ||
+        LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index 799999a..b9a16c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -18,31 +18,43 @@
 
 import android.content.ActivityNotFoundException
 import android.content.Intent
-import android.graphics.Insets
+import android.content.res.Configuration
 import android.os.Bundle
 import android.provider.Settings
-import android.view.View
-import android.view.WindowInsets
-import androidx.activity.BackEventCompat
 import androidx.activity.ComponentActivity
-import androidx.activity.OnBackPressedCallback
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Surface
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
-import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalContext
-import androidx.core.view.updatePadding
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.flowWithLifecycle
 import androidx.lifecycle.lifecycleScope
 import com.android.compose.theme.PlatformTheme
 import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelper
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
 import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.util.dpToPx
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
-import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
 import javax.inject.Inject
 import kotlinx.coroutines.launch
 
@@ -55,52 +67,58 @@
 constructor(private val userTracker: UserTracker, private val viewModel: ShortcutHelperViewModel) :
     ComponentActivity() {
 
-    private val bottomSheetContainer
-        get() = requireViewById<View>(R.id.shortcut_helper_sheet_container)
-
-    private val bottomSheet
-        get() = requireViewById<View>(R.id.shortcut_helper_sheet)
-
-    private val bottomSheetBehavior
-        get() = BottomSheetBehavior.from(bottomSheet)
-
     override fun onCreate(savedInstanceState: Bundle?) {
         setupEdgeToEdge()
         super.onCreate(savedInstanceState)
-        setContentView(R.layout.activity_keyboard_shortcut_helper)
-        setUpWidth()
-        expandBottomSheet()
-        setUpInsets()
-        setUpPredictiveBack()
-        setUpSheetDismissListener()
-        setUpDismissOnTouchOutside()
-        setUpComposeView()
+        setContent { Content() }
         observeFinishRequired()
         viewModel.onViewOpened()
     }
 
-    private fun setUpWidth() {
-        // we override this because when maxWidth isn't specified, material imposes a max width
-        // constraint on bottom sheets on larger screens which is smaller than our desired width.
-        bottomSheetBehavior.maxWidth =
-            resources.getDimension(R.dimen.shortcut_helper_width).dpToPx(resources).toInt()
+    @Composable
+    private fun Content() {
+        CompositionLocalProvider(LocalContext provides userTracker.userContext) {
+            PlatformTheme { BottomSheet { finish() } }
+        }
     }
 
-    private fun setUpComposeView() {
-        requireViewById<ComposeView>(R.id.shortcut_helper_compose_container).apply {
-            setContent {
-                CompositionLocalProvider(LocalContext provides userTracker.userContext) {
-                    PlatformTheme {
-                        val shortcutsUiState by
-                            viewModel.shortcutsUiState.collectAsStateWithLifecycle()
-                        ShortcutHelper(
-                            shortcutsUiState = shortcutsUiState,
-                            onKeyboardSettingsClicked = ::onKeyboardSettingsClicked,
-                            onSearchQueryChanged = { viewModel.onSearchQueryChanged(it) },
-                        )
-                    }
-                }
-            }
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Composable
+    private fun BottomSheet(onDismiss: () -> Unit) {
+        ModalBottomSheet(
+            onDismissRequest = { onDismiss() },
+            modifier =
+                Modifier.width(getWidth()).padding(top = getTopPadding()).onKeyEvent {
+                    if (it.key == Key.Escape) {
+                        onDismiss()
+                        true
+                    } else false
+                },
+            sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
+            dragHandle = { DragHandle() },
+        ) {
+            val shortcutsUiState by viewModel.shortcutsUiState.collectAsStateWithLifecycle()
+            ShortcutHelper(
+                shortcutsUiState = shortcutsUiState,
+                onKeyboardSettingsClicked = ::onKeyboardSettingsClicked,
+                onSearchQueryChanged = { viewModel.onSearchQueryChanged(it) },
+            )
+        }
+    }
+
+    @Composable
+    fun DragHandle() {
+        val dragHandleContentDescription =
+            stringResource(id = R.string.shortcut_helper_content_description_drag_handle)
+        Surface(
+            modifier =
+                Modifier.padding(top = 16.dp, bottom = 6.dp).semantics {
+                    contentDescription = dragHandleContentDescription
+                },
+            color = MaterialTheme.colorScheme.outlineVariant,
+            shape = MaterialTheme.shapes.extraLarge,
+        ) {
+            Box(Modifier.size(width = 32.dp, height = 4.dp))
         }
     }
 
@@ -139,81 +157,27 @@
         window.setDecorFitsSystemWindows(false)
     }
 
-    private fun setUpInsets() {
-        bottomSheetContainer.setOnApplyWindowInsetsListener { _, insets ->
-            val safeDrawingInsets = insets.safeDrawing
-            // Make sure the bottom sheet is not covered by the status bar.
-            bottomSheetBehavior.maxHeight =
-                windowManager.maximumWindowMetrics.bounds.height() - safeDrawingInsets.top
-            // Make sure the contents inside of the bottom sheet are not hidden by system bars, or
-            // cutouts.
-            bottomSheet.updatePadding(
-                left = safeDrawingInsets.left,
-                right = safeDrawingInsets.right,
-                bottom = safeDrawingInsets.bottom,
-            )
-            // The bottom sheet has to be expanded only after setting up insets, otherwise there is
-            // a bug and it will not use full height.
-            expandBottomSheet()
-
-            // Return CONSUMED if you don't want want the window insets to keep passing
-            // down to descendant views.
-            WindowInsets.CONSUMED
-        }
+    @Composable
+    private fun getTopPadding(): Dp {
+        return if (hasCompactWindowSize()) DefaultTopPadding else LargeScreenTopPadding
     }
 
-    private fun expandBottomSheet() {
-        bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
-        bottomSheetBehavior.skipCollapsed = true
-    }
-
-    private fun setUpPredictiveBack() {
-        val onBackPressedCallback =
-            object : OnBackPressedCallback(/* enabled= */ true) {
-                override fun handleOnBackStarted(backEvent: BackEventCompat) {
-                    bottomSheetBehavior.startBackProgress(backEvent)
-                }
-
-                override fun handleOnBackProgressed(backEvent: BackEventCompat) {
-                    bottomSheetBehavior.updateBackProgress(backEvent)
-                }
-
-                override fun handleOnBackPressed() {
-                    bottomSheetBehavior.handleBackInvoked()
-                }
-
-                override fun handleOnBackCancelled() {
-                    bottomSheetBehavior.cancelBackProgress()
-                }
+    @Composable
+    private fun getWidth(): Dp {
+        return if (hasCompactWindowSize()) {
+            DefaultWidth
+        } else
+            when (LocalConfiguration.current.orientation) {
+                Configuration.ORIENTATION_LANDSCAPE -> LargeScreenWidthLandscape
+                else -> LargeScreenWidthPortrait
             }
-        onBackPressedDispatcher.addCallback(
-            owner = this,
-            onBackPressedCallback = onBackPressedCallback,
-        )
     }
 
-    private fun setUpSheetDismissListener() {
-        bottomSheetBehavior.addBottomSheetCallback(
-            object : BottomSheetCallback() {
-                override fun onStateChanged(bottomSheet: View, newState: Int) {
-                    if (newState == STATE_HIDDEN) {
-                        finish()
-                    }
-                }
-
-                override fun onSlide(bottomSheet: View, slideOffset: Float) {}
-            }
-        )
-    }
-
-    private fun setUpDismissOnTouchOutside() {
-        bottomSheetContainer.setOnClickListener { finish() }
+    companion object {
+        private val DefaultTopPadding = 64.dp
+        private val LargeScreenTopPadding = 72.dp
+        private val DefaultWidth = 412.dp
+        private val LargeScreenWidthPortrait = 704.dp
+        private val LargeScreenWidthLandscape = 864.dp
     }
 }
-
-private val WindowInsets.safeDrawing
-    get() =
-        getInsets(WindowInsets.Type.systemBars())
-            .union(getInsets(WindowInsets.Type.displayCutout()))
-
-private fun Insets.union(insets: Insets): Insets = Insets.max(this, insets)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 68a252b..654c763 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -515,7 +515,13 @@
     }
 
     protected void notifyChange() {
-        mBgHandler.post(() -> mContentResolver.notifyChange(mSliceUri, null /* observer */));
+        mBgHandler.post(() -> {
+            try {
+                mContentResolver.notifyChange(mSliceUri, null /* observer */);
+            } catch (Exception e) {
+                Log.e(TAG, "Error on mContentResolver.notifyChange()", e);
+            }
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 416eaba..063adc8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -63,6 +63,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.KeyguardIndicationController
 import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationLockscreenScrimViewModel
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -102,6 +103,7 @@
     private val keyguardClockViewModel: KeyguardClockViewModel,
     private val smartspaceViewModel: KeyguardSmartspaceViewModel,
     private val lockscreenContentViewModelFactory: LockscreenContentViewModel.Factory,
+    private val notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory,
     private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
     private val clockInteractor: KeyguardClockInteractor,
     private val keyguardViewMediator: KeyguardViewMediator,
@@ -207,6 +209,7 @@
     private fun createLockscreen(
         context: Context,
         viewModelFactory: LockscreenContentViewModel.Factory,
+        notificationScrimViewModelFactory: NotificationLockscreenScrimViewModel.Factory,
         blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
     ): View {
         val sceneBlueprints =
@@ -222,6 +225,8 @@
                         with(
                             LockscreenContent(
                                 viewModelFactory = viewModelFactory,
+                                notificationScrimViewModelFactory =
+                                    notificationScrimViewModelFactory,
                                 blueprints = sceneBlueprints,
                                 clockInteractor = clockInteractor,
                             )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ba23eb3..0a38ce0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -3314,7 +3314,11 @@
 
         setShowingLocked(false, "onKeyguardExitFinished: " + reason);
         mWakeAndUnlocking = false;
-        mDismissCallbackRegistry.notifyDismissSucceeded();
+
+        if (!KeyguardWmStateRefactor.isEnabled()) {
+            mDismissCallbackRegistry.notifyDismissSucceeded();
+        }
+
         resetKeyguardDonePendingLocked();
         mHideAnimationRun = false;
         adjustStatusBarLocked();
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 49303e0..130242f 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
@@ -58,6 +58,7 @@
 import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.mapLatest
@@ -486,19 +487,34 @@
 
     override val isDreaming: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
-    override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
-        val callback =
-            object : StatusBarStateController.StateListener {
-                override fun onDozeAmountChanged(linear: Float, eased: Float) {
-                    trySendWithFailureLogging(linear, TAG, "updated dozeAmount")
-                }
+    private val _preSceneLinearDozeAmount: Flow<Float> =
+        if (SceneContainerFlag.isEnabled) {
+            emptyFlow()
+        } else {
+            conflatedCallbackFlow {
+                val callback =
+                    object : StatusBarStateController.StateListener {
+                        override fun onDozeAmountChanged(linear: Float, eased: Float) {
+                            trySendWithFailureLogging(linear, TAG, "updated dozeAmount")
+                        }
+                    }
+
+                statusBarStateController.addCallback(callback)
+                trySendWithFailureLogging(
+                    statusBarStateController.dozeAmount,
+                    TAG,
+                    "initial dozeAmount"
+                )
+
+                awaitClose { statusBarStateController.removeCallback(callback) }
             }
+        }
 
-        statusBarStateController.addCallback(callback)
-        trySendWithFailureLogging(statusBarStateController.dozeAmount, TAG, "initial dozeAmount")
-
-        awaitClose { statusBarStateController.removeCallback(callback) }
-    }
+    override val linearDozeAmount: Flow<Float>
+        get() {
+            SceneContainerFlag.assertInLegacyMode()
+            return _preSceneLinearDozeAmount
+        }
 
     override val dozeTransitionModel: Flow<DozeTransitionModel> = conflatedCallbackFlow {
         val callback =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index b0820a7..8c7fe5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -150,7 +150,9 @@
                         if (!SceneContainerFlag.isEnabled) {
                             startTransitionTo(KeyguardState.GLANCEABLE_HUB)
                         }
-                    } else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
+                    } else if (isCommunalAvailable && dreamManager.canStartDreaming(false)) {
+                        // Using false for isScreenOn as canStartDreaming returns false if any
+                        // dream, including doze, is active.
                         // This case handles tapping the power button to transition through
                         // dream -> off -> hub.
                         if (!SceneContainerFlag.isEnabled) {
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 2434b29..9a0a858 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
@@ -17,9 +17,12 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.animation.ValueAnimator
+import android.annotation.SuppressLint
+import android.app.DreamManager
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.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
@@ -60,10 +63,12 @@
     @Main mainDispatcher: CoroutineDispatcher,
     keyguardInteractor: KeyguardInteractor,
     private val glanceableHubTransitions: GlanceableHubTransitions,
+    private val communalInteractor: CommunalInteractor,
     private val communalSceneInteractor: CommunalSceneInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     powerInteractor: PowerInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+    private val dreamManager: DreamManager,
     private val deviceEntryInteractor: DeviceEntryInteractor,
 ) :
     TransitionInteractor(
@@ -76,6 +81,7 @@
         keyguardInteractor = keyguardInteractor,
     ) {
 
+    @SuppressLint("MissingPermission")
     override fun start() {
         listenForDreamingToAlternateBouncer()
         listenForDreamingToOccluded()
@@ -86,6 +92,8 @@
         listenForTransitionToCamera(scope, keyguardInteractor)
         if (!communalSceneKtfRefactor()) {
             listenForDreamingToGlanceableHub()
+        } else {
+            listenForDreamingToGlanceableHubFromPowerButton()
         }
         listenForDreamingToPrimaryBouncer()
     }
@@ -112,6 +120,34 @@
         }
     }
 
+    /**
+     * Normally when pressing power button from the dream, the devices goes from DREAMING to DOZING,
+     * then [FromDozingTransitionInteractor] handles the transition to GLANCEABLE_HUB. However if
+     * the power button is pressed quickly, we may need to go directly from DREAMING to
+     * GLANCEABLE_HUB as the transition to DOZING has not occurred yet.
+     */
+    @SuppressLint("MissingPermission")
+    private fun listenForDreamingToGlanceableHubFromPowerButton() {
+        if (!communalSettingsInteractor.isCommunalFlagEnabled()) return
+        if (SceneContainerFlag.isEnabled) return
+        scope.launch {
+            powerInteractor.isAwake
+                .debounce(50L)
+                .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
+                .sample(communalInteractor.isCommunalAvailable)
+                .collect { isCommunalAvailable ->
+                    if (isCommunalAvailable && dreamManager.canStartDreaming(false)) {
+                        // This case handles tapping the power button to transition through
+                        // dream -> off -> hub.
+                        communalSceneInteractor.snapToScene(
+                            newScene = CommunalScenes.Communal,
+                            loggingReason = "from dreaming to hub",
+                        )
+                    }
+                }
+        }
+    }
+
     private fun listenForDreamingToPrimaryBouncer() {
         // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
@@ -144,7 +180,7 @@
                     } else {
                         startTransitionTo(
                             KeyguardState.LOCKSCREEN,
-                            ownerReason = "Dream has ended and device is awake"
+                            ownerReason = "Dream has ended and device is awake",
                         )
                     }
                 }
@@ -158,15 +194,14 @@
             scope.launch {
                 combine(
                         keyguardInteractor.isKeyguardOccluded,
-                        keyguardInteractor.isAbleToDream
-                            // Debounce the dreaming signal since there is a race condition between
-                            // the occluded and dreaming signals. We therefore add a small delay
-                            // to give enough time for occluded to flip to false when the dream
-                            // ends, to avoid transitioning to OCCLUDED erroneously when exiting
-                            // the dream.
-                            .debounce(100.milliseconds),
-                        ::Pair
+                        keyguardInteractor.isDreaming,
+                        ::Pair,
                     )
+                    // Debounce signals since there is a race condition between the occluded and
+                    // dreaming signals when starting or stopping dreaming. We therefore add a small
+                    // delay to give enough time for occluded to flip to false when the dream
+                    // ends, to avoid transitioning to OCCLUDED erroneously when exiting the dream.
+                    .debounce(100.milliseconds)
                     .filterRelevantKeyguardStateAnd { (isOccluded, isDreaming) ->
                         isOccluded && !isDreaming
                     }
@@ -194,12 +229,12 @@
                     if (dismissable) {
                         startTransitionTo(
                             KeyguardState.GONE,
-                            ownerReason = "No longer dreaming; dismissable"
+                            ownerReason = "No longer dreaming; dismissable",
                         )
                     } else {
                         startTransitionTo(
                             KeyguardState.LOCKSCREEN,
-                            ownerReason = "No longer dreaming"
+                            ownerReason = "No longer dreaming",
                         )
                     }
                 }
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 7759298..6b6a3dce 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
@@ -202,15 +202,15 @@
             scope.launch {
                 combine(
                         keyguardInteractor.isKeyguardOccluded,
-                        keyguardInteractor.isAbleToDream
-                            // Debounce the dreaming signal since there is a race condition between
-                            // the occluded and dreaming signals. We therefore add a small delay
-                            // to give enough time for occluded to flip to false when the dream
-                            // ends, to avoid transitioning to OCCLUDED erroneously when exiting
-                            // the dream.
-                            .debounce(100.milliseconds),
+                        keyguardInteractor.isDreaming,
                         ::Pair,
                     )
+                    // Debounce signals since there is a race condition between the occluded and
+                    // dreaming signals when starting or stopping dreaming. We therefore add a small
+                    // delay to give enough time for occluded to flip to false when the dream
+                    // ends, to avoid transitioning to OCCLUDED erroneously when exiting the dream
+                    // or when the dream starts underneath the hub.
+                    .debounce(200.milliseconds)
                     .sampleFilter(
                         // When launching activities from widgets on the hub, we have a
                         // custom occlusion animation.
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 eb9b07a..18b1495 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
@@ -28,7 +28,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -60,11 +59,11 @@
     transitionInteractor: KeyguardTransitionInteractor,
     val dismissInteractor: KeyguardDismissInteractor,
     @Application private val applicationScope: CoroutineScope,
-    sceneInteractor: Lazy<SceneInteractor>,
     deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>,
     powerInteractor: PowerInteractor,
     alternateBouncerInteractor: AlternateBouncerInteractor,
     shadeInteractor: Lazy<ShadeInteractor>,
+    keyguardInteractor: Lazy<KeyguardInteractor>,
 ) {
     val dismissAction: Flow<DismissAction> = repository.dismissAction
 
@@ -101,20 +100,20 @@
     private val isOnShadeWhileUnlocked: Flow<Boolean> =
         if (SceneContainerFlag.isEnabled) {
             combine(
-                    sceneInteractor.get().currentScene,
+                    shadeInteractor.get().isAnyExpanded,
                     deviceUnlockedInteractor.get().deviceUnlockStatus,
-                ) { scene, unlockStatus ->
-                    unlockStatus.isUnlocked &&
-                        (scene == Scenes.QuickSettings || scene == Scenes.Shade)
+                ) { isAnyExpanded, unlockStatus ->
+                    isAnyExpanded && unlockStatus.isUnlocked
                 }
                 .distinctUntilChanged()
         } else if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) {
             combine(
-                shadeInteractor.get().isAnyExpanded,
-                deviceUnlockedInteractor.get().deviceUnlockStatus,
-            ) { isAnyExpanded, deviceUnlockStatus ->
-                isAnyExpanded && deviceUnlockStatus.isUnlocked
-            }
+                    shadeInteractor.get().isAnyExpanded,
+                    keyguardInteractor.get().isKeyguardDismissible,
+                ) { isAnyExpanded, keyguardDismissible ->
+                    isAnyExpanded && keyguardDismissible
+                }
+                .distinctUntilChanged()
         } else {
             flow {
                 error(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index 4457f1d..9b9bdd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -23,12 +23,10 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.DismissCallbackRegistry
-import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
-import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
@@ -37,7 +35,6 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
@@ -59,7 +56,6 @@
     trustRepository: TrustRepository,
     alternateBouncerInteractor: AlternateBouncerInteractor,
     powerInteractor: PowerInteractor,
-    keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) {
     /*
      * Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -165,14 +161,4 @@
             }
         }
     }
-
-    init {
-        if (KeyguardWmStateRefactor.isEnabled) {
-            scope.launch {
-                keyguardTransitionInteractor.currentKeyguardState
-                    .filter { it == KeyguardState.GONE }
-                    .collect { dismissCallbackRegistry.notifyDismissSucceeded() }
-            }
-        }
-    }
 }
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 e444092..e6ee112 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
@@ -140,12 +140,6 @@
         _notificationPlaceholderBounds.value = position
     }
 
-    /**
-     * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
-     * all.
-     */
-    val dozeAmount: Flow<Float> = repository.linearDozeAmount
-
     /** Whether the system is in doze mode. */
     val isDozing: StateFlow<Boolean> = repository.isDozing
 
@@ -155,6 +149,23 @@
     /** Whether Always-on Display mode is available. */
     val isAodAvailable: StateFlow<Boolean> = repository.isAodAvailable
 
+    /**
+     * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
+     * all.
+     */
+    val dozeAmount: Flow<Float> =
+        if (SceneContainerFlag.isEnabled) {
+            isAodAvailable.flatMapLatest { isAodAvailable ->
+                if (isAodAvailable) {
+                    keyguardTransitionInteractor.transitionValue(AOD)
+                } else {
+                    keyguardTransitionInteractor.transitionValue(DOZING)
+                }
+            }
+        } else {
+            repository.linearDozeAmount
+        }
+
     /** Doze transition information. */
     val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
 
@@ -164,8 +175,8 @@
     val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
 
     /**
-     * Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
-     * but not vice-versa. Also accounts for [isDreamingWithOverlay]
+     * Whether the system is dreaming. [KeyguardRepository.isDreaming] will be always be true when
+     * [isDozing] is true, but not vice-versa. Also accounts for [isDreamingWithOverlay].
      */
     val isDreaming: StateFlow<Boolean> =
         merge(repository.isDreaming, repository.isDreamingWithOverlay)
@@ -175,6 +186,9 @@
                 initialValue = false,
             )
 
+    /** Whether any dreaming is running, including the doze dream. */
+    val isDreamingAny: Flow<Boolean> = repository.isDreaming
+
     /** Whether the system is dreaming and the active dream is hosted in lockscreen */
     val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt
index 7fd348b..6fe4ff5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.app.trust.TrustManager
 import android.os.DeadObjectException
 import android.os.RemoteException
 import com.android.internal.policy.IKeyguardStateCallback
@@ -24,6 +25,7 @@
 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.DismissCallbackRegistry
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -32,7 +34,6 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
@@ -53,6 +54,9 @@
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val trustInteractor: TrustInteractor,
     private val simBouncerInteractor: SimBouncerInteractor,
+    private val dismissCallbackRegistry: DismissCallbackRegistry,
+    private val wmLockscreenVisibilityInteractor: WindowManagerLockscreenVisibilityInteractor,
+    private val trustManager: TrustManager,
 ) : CoreStartable {
     private val callbacks = mutableListOf<IKeyguardStateCallback>()
 
@@ -62,28 +66,31 @@
         }
 
         applicationScope.launch {
-            combine(
-                    selectedUserInteractor.selectedUser,
-                    keyguardTransitionInteractor.currentKeyguardState,
-                    keyguardTransitionInteractor.startedKeyguardTransitionStep,
-                    ::Triple,
-                )
-                .collectLatest { (selectedUser, _, _) ->
-                    val iterator = callbacks.iterator()
-                    withContext(backgroundDispatcher) {
-                        while (iterator.hasNext()) {
-                            val callback = iterator.next()
-                            try {
-                                callback.onShowingStateChanged(!isIdleInGone(), selectedUser)
-                                callback.onInputRestrictedStateChanged(!isIdleInGone())
-                            } catch (e: RemoteException) {
-                                if (e is DeadObjectException) {
-                                    iterator.remove()
-                                }
+            wmLockscreenVisibilityInteractor.lockscreenVisibility.collectLatest { visible ->
+                val iterator = callbacks.iterator()
+                withContext(backgroundDispatcher) {
+                    while (iterator.hasNext()) {
+                        val callback = iterator.next()
+                        try {
+                            callback.onShowingStateChanged(
+                                visible,
+                                selectedUserInteractor.getSelectedUserId(),
+                            )
+                            callback.onInputRestrictedStateChanged(visible)
+
+                            trustManager.reportKeyguardShowingChanged()
+
+                            if (!visible) {
+                                dismissCallbackRegistry.notifyDismissSucceeded()
+                            }
+                        } catch (e: RemoteException) {
+                            if (e is DeadObjectException) {
+                                iterator.remove()
                             }
                         }
                     }
                 }
+            }
         }
 
         applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index b89eb27..cf9d60f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -19,6 +19,7 @@
 import com.android.keyguard.logging.KeyguardLogger
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.log.core.LogLevel.VERBOSE
 import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -44,6 +45,7 @@
     private val powerInteractor: PowerInteractor,
     private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
     private val keyguardRootViewModel: KeyguardRootViewModel,
+    private val aodBurnInViewModel: AodBurnInViewModel,
     private val shadeInteractor: ShadeInteractor,
     private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
 ) {
@@ -132,7 +134,7 @@
         }
 
         scope.launch {
-            keyguardRootViewModel.burnInModel.debounce(20L).collect {
+            aodBurnInViewModel.movement.debounce(20L).collect {
                 logger.log(TAG, VERBOSE, "BurnInModel (debounced)", it)
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index ba9f018..5f76f64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.keyguard.shared.model.ClockSize
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -58,6 +59,7 @@
         keyguardClockInteractor: KeyguardClockInteractor,
         blueprintInteractor: KeyguardBlueprintInteractor,
         rootViewModel: KeyguardRootViewModel,
+        aodBurnInViewModel: AodBurnInViewModel,
     ): DisposableHandle {
         val disposables = DisposableHandles()
         disposables +=
@@ -78,7 +80,7 @@
                             updateBurnInLayer(
                                 keyguardRootView,
                                 viewModel,
-                                viewModel.clockSize.value
+                                viewModel.clockSize.value,
                             )
                             applyConstraints(clockSection, keyguardRootView, true)
                         }
@@ -114,7 +116,7 @@
                         if (!MigrateClocksToBlueprint.isEnabled) return@launch
                         combine(
                                 viewModel.hasAodIcons,
-                                rootViewModel.isNotifIconContainerVisible.map { it.value }
+                                rootViewModel.isNotifIconContainerVisible.map { it.value },
                             ) { hasIcon, isVisible ->
                                 hasIcon && isVisible
                             }
@@ -130,13 +132,13 @@
 
                     launch {
                         if (!MigrateClocksToBlueprint.isEnabled) return@launch
-                        rootViewModel.burnInModel.collect { burnInModel ->
+                        aodBurnInViewModel.movement.collect { burnInModel ->
                             viewModel.currentClock.value?.let {
                                 it.largeClock.layout.applyAodBurnIn(
                                     AodClockBurnInModel(
                                         translationX = burnInModel.translationX.toFloat(),
                                         translationY = burnInModel.translationY.toFloat(),
-                                        scale = burnInModel.scale
+                                        scale = burnInModel.scale,
                                     )
                                 )
                             }
@@ -175,7 +177,7 @@
     private fun cleanupClockViews(
         currentClock: ClockController?,
         rootView: ConstraintLayout,
-        burnInLayer: Layer?
+        burnInLayer: Layer?,
     ) {
         if (lastClock == currentClock) {
             return
@@ -192,10 +194,7 @@
     }
 
     @VisibleForTesting
-    fun addClockViews(
-        clockController: ClockController?,
-        rootView: ConstraintLayout,
-    ) {
+    fun addClockViews(clockController: ClockController?, rootView: ConstraintLayout) {
         // We'll collect the same clock when exiting wallpaper picker without changing clock
         // so we need to remove clock views from parent before addView again
         clockController?.let { clock ->
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 b5f6b41..ee2ee52 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
@@ -45,7 +45,6 @@
 import com.android.keyguard.AuthInteractionProperties
 import com.android.systemui.Flags
 import com.android.systemui.Flags.msdlFeedback
-import com.android.systemui.Flags.newAodTransition
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.shared.model.TintedIcon
@@ -261,10 +260,7 @@
                             ->
                             if (biometricMessage?.message != null) {
                                 chipbarCoordinator!!.displayView(
-                                    createChipbarInfo(
-                                        biometricMessage.message,
-                                        R.drawable.ic_lock,
-                                    )
+                                    createChipbarInfo(biometricMessage.message, R.drawable.ic_lock)
                                 )
                             } else {
                                 chipbarCoordinator!!.removeView(ID, "occludingAppMsgNull")
@@ -327,6 +323,9 @@
                                     .getDimensionPixelSize(R.dimen.shelf_appear_translation)
                                     .stateIn(this)
                             viewModel.isNotifIconContainerVisible.collect { isVisible ->
+                                if (isVisible.value) {
+                                    blueprintViewModel.refreshBlueprint()
+                                }
                                 childViews[aodNotificationIconContainerId]
                                     ?.setAodNotifIconContainerIsVisible(
                                         isVisible,
@@ -382,7 +381,7 @@
                                 if (msdlFeedback()) {
                                     msdlPlayer?.playToken(
                                         MSDLToken.UNLOCK,
-                                        authInteractionProperties
+                                        authInteractionProperties,
                                     )
                                 } else {
                                     vibratorHelper.performHapticFeedback(
@@ -398,7 +397,7 @@
                                 if (msdlFeedback()) {
                                     msdlPlayer?.playToken(
                                         MSDLToken.FAILURE,
-                                        authInteractionProperties
+                                        authInteractionProperties,
                                     )
                                 } else {
                                     vibratorHelper.performHapticFeedback(
@@ -414,7 +413,10 @@
 
         if (MigrateClocksToBlueprint.isEnabled) {
             burnInParams.update { current ->
-                current.copy(translationY = { childViews[burnInLayerId]?.translationY })
+                current.copy(
+                    translationX = { childViews[burnInLayerId]?.translationX },
+                    translationY = { childViews[burnInLayerId]?.translationY },
+                )
             }
         }
 
@@ -425,7 +427,7 @@
                     blueprintViewModel,
                     clockViewModel,
                     childViews,
-                    burnInParams
+                    burnInParams,
                 )
             )
 
@@ -464,11 +466,7 @@
      */
     private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo {
         return ChipbarInfo(
-            startIcon =
-                TintedIcon(
-                    Icon.Resource(icon, null),
-                    ChipbarInfo.DEFAULT_ICON_TINT,
-                ),
+            startIcon = TintedIcon(Icon.Resource(icon, null), ChipbarInfo.DEFAULT_ICON_TINT),
             text = Text.Loaded(message),
             endItem = null,
             vibrationEffect = null,
@@ -499,7 +497,7 @@
             oldLeft: Int,
             oldTop: Int,
             oldRight: Int,
-            oldBottom: Int
+            oldBottom: Int,
         ) {
             // After layout, ensure the notifications are positioned correctly
             childViews[nsslPlaceholderId]?.let { notificationListPlaceholder ->
@@ -515,7 +513,7 @@
                 viewModel.onNotificationContainerBoundsChanged(
                     notificationListPlaceholder.top.toFloat(),
                     notificationListPlaceholder.bottom.toFloat(),
-                    animate = shouldAnimate
+                    animate = shouldAnimate,
                 )
             }
 
@@ -531,7 +529,7 @@
                                         Int.MAX_VALUE
                                     } else {
                                         view.getTop()
-                                    }
+                                    },
                                 )
                             }
                         } else {
@@ -597,7 +595,7 @@
                         View.INVISIBLE
                     }
             }
-            newAodTransition() -> {
+            else -> {
                 animateInIconTranslation()
                 if (isVisible.value) {
                     CrossFadeHelper.fadeIn(this, animatorListener)
@@ -605,52 +603,6 @@
                     CrossFadeHelper.fadeOut(this, animatorListener)
                 }
             }
-            !isVisible.value -> {
-                // Let's make sure the icon are translated to 0, since we cancelled it above
-                animateInIconTranslation()
-                CrossFadeHelper.fadeOut(this, animatorListener)
-            }
-            visibility != View.VISIBLE -> {
-                // No fading here, let's just appear the icons instead!
-                visibility = View.VISIBLE
-                alpha = 1f
-                appearIcons(
-                    animate = screenOffAnimationController.shouldAnimateAodIcons(),
-                    iconsAppearTranslationPx,
-                    animatorListener,
-                )
-            }
-            else -> {
-                // Let's make sure the icons are translated to 0, since we cancelled it above
-                animateInIconTranslation()
-                // We were fading out, let's fade in instead
-                CrossFadeHelper.fadeIn(this, animatorListener)
-            }
-        }
-    }
-
-    private fun View.appearIcons(
-        animate: Boolean,
-        iconAppearTranslation: Int,
-        animatorListener: Animator.AnimatorListener,
-    ) {
-        if (animate) {
-            if (!MigrateClocksToBlueprint.isEnabled) {
-                translationY = -iconAppearTranslation.toFloat()
-            }
-            alpha = 0f
-            animate()
-                .alpha(1f)
-                .setInterpolator(Interpolators.LINEAR)
-                .setDuration(AOD_ICONS_APPEAR_DURATION)
-                .apply { if (MigrateClocksToBlueprint.isEnabled) animateInIconTranslation() }
-                .setListener(animatorListener)
-                .start()
-        } else {
-            alpha = 1.0f
-            if (!MigrateClocksToBlueprint.isEnabled) {
-                translationY = 0f
-            }
         }
     }
 
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 0b8f741..cef9a4e 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
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.ui.preview
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.app.WallpaperColors
 import android.content.BroadcastReceiver
 import android.content.Context
@@ -187,7 +188,7 @@
     private var themeStyle: Style? = null
 
     init {
-        coroutineScope = CoroutineScope(applicationScope.coroutineContext + Job())
+        coroutineScope = CoroutineScope(applicationScope.coroutineContext + Job() + createCoroutineTracingContext("KeyguardPreviewRenderer"))
         disposables += DisposableHandle { coroutineScope.cancel() }
         clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index be6b0eb..ff84826 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardClockViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
@@ -49,25 +50,17 @@
 import javax.inject.Inject
 import kotlinx.coroutines.DisposableHandle
 
-internal fun ConstraintSet.setVisibility(
-    views: Iterable<View>,
-    visibility: Int,
-) = views.forEach { view -> this.setVisibility(view.id, visibility) }
+internal fun ConstraintSet.setVisibility(views: Iterable<View>, visibility: Int) =
+    views.forEach { view -> this.setVisibility(view.id, visibility) }
 
-internal fun ConstraintSet.setAlpha(
-    views: Iterable<View>,
-    alpha: Float,
-) = views.forEach { view -> this.setAlpha(view.id, alpha) }
+internal fun ConstraintSet.setAlpha(views: Iterable<View>, alpha: Float) =
+    views.forEach { view -> this.setAlpha(view.id, alpha) }
 
-internal fun ConstraintSet.setScaleX(
-    views: Iterable<View>,
-    alpha: Float,
-) = views.forEach { view -> this.setScaleX(view.id, alpha) }
+internal fun ConstraintSet.setScaleX(views: Iterable<View>, alpha: Float) =
+    views.forEach { view -> this.setScaleX(view.id, alpha) }
 
-internal fun ConstraintSet.setScaleY(
-    views: Iterable<View>,
-    alpha: Float,
-) = views.forEach { view -> this.setScaleY(view.id, alpha) }
+internal fun ConstraintSet.setScaleY(views: Iterable<View>, alpha: Float) =
+    views.forEach { view -> this.setScaleY(view.id, alpha) }
 
 @SysUISingleton
 class ClockSection
@@ -79,6 +72,7 @@
     val smartspaceViewModel: KeyguardSmartspaceViewModel,
     val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
     private val rootViewModel: KeyguardRootViewModel,
+    private val aodBurnInViewModel: AodBurnInViewModel,
 ) : KeyguardSection() {
     private var disposableHandle: DisposableHandle? = null
 
@@ -97,6 +91,7 @@
                 clockInteractor,
                 blueprintInteractor.get(),
                 rootViewModel,
+                aodBurnInViewModel,
             )
     }
 
@@ -120,7 +115,7 @@
 
     private fun buildConstraints(
         clock: ClockController,
-        constraintSet: ConstraintSet
+        constraintSet: ConstraintSet,
     ): ConstraintSet {
         // Add constraint between rootView and clockContainer
         applyDefaultConstraints(constraintSet)
@@ -136,8 +131,8 @@
             if (!keyguardClockViewModel.isLargeClockVisible.value) {
                 connect(sharedR.id.bc_smartspace_view, TOP, sharedR.id.date_smartspace_view, BOTTOM)
             } else {
-                setScaleX(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
-                setScaleY(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
+                setScaleX(getTargetClockFace(clock).views, aodBurnInViewModel.movement.value.scale)
+                setScaleY(getTargetClockFace(clock).views, aodBurnInViewModel.movement.value.scale)
             }
         }
     }
@@ -156,7 +151,7 @@
                 R.id.weather_clock_bc_smartspace_bottom,
                 Barrier.BOTTOM,
                 getDimen(ENHANCED_SMARTSPACE_HEIGHT),
-                (custR.id.weather_clock_time)
+                (custR.id.weather_clock_time),
             )
             if (
                 rootViewModel.isNotifIconContainerVisible.value.value &&
@@ -168,15 +163,15 @@
                     0,
                     *intArrayOf(
                         R.id.aod_notification_icon_container,
-                        R.id.weather_clock_bc_smartspace_bottom
-                    )
+                        R.id.weather_clock_bc_smartspace_bottom,
+                    ),
                 )
             } else {
                 createBarrier(
                     R.id.weather_clock_date_and_icons_barrier_bottom,
                     Barrier.BOTTOM,
                     0,
-                    *intArrayOf(R.id.weather_clock_bc_smartspace_bottom)
+                    *intArrayOf(R.id.weather_clock_bc_smartspace_bottom),
                 )
             }
         }
@@ -204,7 +199,7 @@
             constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT)
             constrainHeight(
                 R.id.lockscreen_clock_view,
-                context.resources.getDimensionPixelSize(custR.dimen.small_clock_height)
+                context.resources.getDimensionPixelSize(custR.dimen.small_clock_height),
             )
             connect(
                 R.id.lockscreen_clock_view,
@@ -212,7 +207,7 @@
                 PARENT_ID,
                 START,
                 context.resources.getDimensionPixelSize(custR.dimen.clock_padding_start) +
-                    context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
+                    context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal),
             )
             val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
             create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
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 62b4782..c78e0c9 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
@@ -23,6 +23,7 @@
 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.dagger.qualifiers.Application
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -34,13 +35,17 @@
 import com.android.systemui.res.R
 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
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
 
 /**
  * Models UI state for elements that need to apply anti-burn-in tactics when showing in AOD
@@ -50,6 +55,7 @@
 class AodBurnInViewModel
 @Inject
 constructor(
+    @Application private val applicationScope: CoroutineScope,
     private val burnInInteractor: BurnInInteractor,
     private val configurationInteractor: ConfigurationInteractor,
     private val keyguardInteractor: KeyguardInteractor,
@@ -61,91 +67,109 @@
     private val keyguardClockViewModel: KeyguardClockViewModel,
 ) {
     private val TAG = "AodBurnInViewModel"
+    private val burnInParams = MutableStateFlow(BurnInParameters())
 
-    /** All burn-in movement: x,y,scale, to shift items and prevent burn-in */
-    fun movement(
-        burnInParams: BurnInParameters,
-    ): Flow<BurnInModel> {
-        val params =
-            if (burnInParams.minViewY < burnInParams.topInset) {
+    fun updateBurnInParams(params: BurnInParameters) {
+        burnInParams.value =
+            if (params.minViewY < params.topInset) {
                 // minViewY should never be below the inset. Correct it if needed
-                Log.w(TAG, "minViewY is below topInset: $burnInParams")
-                burnInParams.copy(minViewY = burnInParams.topInset)
+                Log.w(TAG, "minViewY is below topInset: $params")
+                params.copy(minViewY = params.topInset)
             } else {
-                burnInParams
+                params
             }
-        return configurationInteractor
-            .dimensionPixelSize(
-                setOf(
-                    R.dimen.keyguard_enter_from_top_translation_y,
-                    R.dimen.keyguard_enter_from_side_translation_x,
-                )
-            )
-            .flatMapLatest { dimens ->
-                combine(
-                    keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
-                    burnIn(params).onStart { emit(BurnInModel()) },
-                    goneToAodTransitionViewModel
-                        .enterFromTopTranslationY(
-                            dimens[R.dimen.keyguard_enter_from_top_translation_y]!!
-                        )
-                        .onStart { emit(StateToValue()) },
-                    goneToAodTransitionViewModel
-                        .enterFromSideTranslationX(
-                            dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
-                        )
-                        .onStart { emit(StateToValue()) },
-                    lockscreenToAodTransitionViewModel
-                        .enterFromSideTranslationX(
-                            dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
-                        )
-                        .onStart { emit(StateToValue()) },
-                    occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
-                        emit(0f)
-                    },
-                    aodToLockscreenTransitionViewModel.translationY(params.translationY).onStart {
-                        emit(StateToValue())
-                    },
-                ) { flows ->
-                    val keyguardTranslationY = flows[0] as Float
-                    val burnInModel = flows[1] as BurnInModel
-                    val goneToAodTranslationY = flows[2] as StateToValue
-                    val goneToAodTranslationX = flows[3] as StateToValue
-                    val lockscreenToAodTranslationX = flows[4] as StateToValue
-                    val occludedToLockscreen = flows[5] as Float
-                    val aodToLockscreen = flows[6] as StateToValue
-
-                    val translationY =
-                        if (aodToLockscreen.transitionState.isTransitioning()) {
-                            aodToLockscreen.value ?: 0f
-                        } else if (goneToAodTranslationY.transitionState.isTransitioning()) {
-                            (goneToAodTranslationY.value ?: 0f) + burnInModel.translationY
-                        } else {
-                            burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
-                        }
-                    val translationX =
-                        burnInModel.translationX +
-                            (goneToAodTranslationX.value ?: 0f) +
-                            (lockscreenToAodTranslationX.value ?: 0f)
-                    burnInModel.copy(
-                        translationX = translationX.toInt(),
-                        translationY = translationY.toInt(),
-                    )
-                }
-            }
-            .distinctUntilChanged()
     }
 
-    private fun burnIn(
-        params: BurnInParameters,
-    ): Flow<BurnInModel> {
+    /** All burn-in movement: x,y,scale, to shift items and prevent burn-in */
+    val movement: StateFlow<BurnInModel> =
+        burnInParams
+            .flatMapLatest { params ->
+                configurationInteractor
+                    .dimensionPixelSize(
+                        setOf(
+                            R.dimen.keyguard_enter_from_top_translation_y,
+                            R.dimen.keyguard_enter_from_side_translation_x,
+                        )
+                    )
+                    .flatMapLatest { dimens ->
+                        combine(
+                            keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
+                            burnIn(params).onStart { emit(BurnInModel()) },
+                            goneToAodTransitionViewModel
+                                .enterFromTopTranslationY(
+                                    dimens[R.dimen.keyguard_enter_from_top_translation_y]!!
+                                )
+                                .onStart { emit(StateToValue()) },
+                            goneToAodTransitionViewModel
+                                .enterFromSideTranslationX(
+                                    dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
+                                )
+                                .onStart { emit(StateToValue()) },
+                            lockscreenToAodTransitionViewModel
+                                .enterFromSideTranslationX(
+                                    dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
+                                )
+                                .onStart { emit(StateToValue()) },
+                            occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
+                                emit(0f)
+                            },
+                            aodToLockscreenTransitionViewModel
+                                .translationX(params.translationX)
+                                .onStart { emit(StateToValue()) },
+                            aodToLockscreenTransitionViewModel
+                                .translationY(params.translationY)
+                                .onStart { emit(StateToValue()) },
+                        ) { flows ->
+                            val keyguardTranslationY = flows[0] as Float
+                            val burnInModel = flows[1] as BurnInModel
+                            val goneToAodTranslationY = flows[2] as StateToValue
+                            val goneToAodTranslationX = flows[3] as StateToValue
+                            val lockscreenToAodTranslationX = flows[4] as StateToValue
+                            val occludedToLockscreen = flows[5] as Float
+                            val aodToLockscreenTranslationX = flows[6] as StateToValue
+                            val aodToLockscreenTranslationY = flows[7] as StateToValue
+
+                            val translationY =
+                                if (aodToLockscreenTranslationY.transitionState.isTransitioning()) {
+                                    aodToLockscreenTranslationY.value ?: 0f
+                                } else if (
+                                    goneToAodTranslationY.transitionState.isTransitioning()
+                                ) {
+                                    (goneToAodTranslationY.value ?: 0f) + burnInModel.translationY
+                                } else {
+                                    burnInModel.translationY +
+                                        occludedToLockscreen +
+                                        keyguardTranslationY
+                                }
+                            val translationX =
+                                if (aodToLockscreenTranslationX.transitionState.isTransitioning()) {
+                                    aodToLockscreenTranslationX.value ?: 0f
+                                } else {
+                                    burnInModel.translationX +
+                                        (goneToAodTranslationX.value ?: 0f) +
+                                        (lockscreenToAodTranslationX.value ?: 0f)
+                                }
+                            burnInModel.copy(
+                                translationX = translationX.toInt(),
+                                translationY = translationY.toInt(),
+                            )
+                        }
+                    }
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = BurnInModel(),
+            )
+
+    private fun burnIn(params: BurnInParameters): Flow<BurnInModel> {
         return combine(
             keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).map {
                 Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it)
             },
             burnInInteractor.burnIn(
                 xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
-                yDimenResourceId = R.dimen.burn_in_prevention_offset_y
+                yDimenResourceId = R.dimen.burn_in_prevention_offset_y,
             ),
         ) { interpolated, burnIn ->
             val useAltAod =
@@ -168,7 +192,7 @@
                 translationX = MathUtils.lerp(0, burnIn.translationX, interpolated).toInt(),
                 translationY = translationY,
                 scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolated),
-                scaleClockOnly = useScaleOnly
+                scaleClockOnly = useScaleOnly,
             )
         }
     }
@@ -181,7 +205,9 @@
     /** The min y-value of the visible elements on lockscreen */
     val minViewY: Int = Int.MAX_VALUE,
     /** The current y translation of the view */
-    val translationY: () -> Float? = { null }
+    val translationY: () -> Float? = { null },
+    /** The current x translation of the view */
+    val translationX: () -> Float? = { null },
 )
 
 /**
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 6b22c0f..6eeab8d 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
@@ -39,10 +39,8 @@
 @SysUISingleton
 class AodToLockscreenTransitionViewModel
 @Inject
-constructor(
-    shadeInteractor: ShadeInteractor,
-    animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(shadeInteractor: ShadeInteractor, animationFlow: KeyguardTransitionAnimationFlow) :
+    DeviceEntryIconTransition {
 
     private val transitionAnimation =
         animationFlow.setup(
@@ -54,7 +52,7 @@
 
     /**
      * Begin the transition from wherever the y-translation value is currently. This helps ensure a
-     * smooth transition if a transition in canceled.
+     * smooth transition if the prior transition was canceled.
      */
     fun translationY(currentTranslationY: () -> Float?): Flow<StateToValue> {
         var startValue = 0f
@@ -65,6 +63,19 @@
         )
     }
 
+    /**
+     * Begin the transition from wherever the x-translation value is currently. This helps ensure a
+     * smooth transition if the prior transition was canceled.
+     */
+    fun translationX(currentTranslationX: () -> Float?): Flow<StateToValue> {
+        var startValue = 0f
+        return transitionAnimation.sharedFlowWithState(
+            duration = 500.milliseconds,
+            onStart = { startValue = currentTranslationX() ?: 0f },
+            onStep = { MathUtils.lerp(startValue, 0f, FAST_OUT_SLOW_IN.getInterpolation(it)) },
+        )
+    }
+
     /** Ensure alpha is set to be visible */
     fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
         var startAlpha = 1f
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 aee34e1..1e42e19 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
@@ -26,6 +26,7 @@
 import com.android.systemui.scene.shared.model.Scenes
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 
 @SysUISingleton
 class DozingToGlanceableHubTransitionViewModel
@@ -35,10 +36,16 @@
         animationFlow
             .setup(
                 duration = TO_GLANCEABLE_HUB_DURATION,
-                edge = Edge.create(DOZING, Scenes.Communal)
+                edge = Edge.create(DOZING, Scenes.Communal),
             )
             .setupWithoutSceneContainer(edge = Edge.create(DOZING, GLANCEABLE_HUB))
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
+
+    /**
+     * Hide notifications when transitioning directly from dozing to hub, such as when pressing
+     * power button when dozing and docked.
+     */
+    val notificationAlpha: Flow<Float> = flowOf(0f)
 }
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 c6efcfa..4cf3c4e 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
@@ -25,20 +25,18 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 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 javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
-data class TransitionData(
-    val config: Config,
-    val start: Long = System.currentTimeMillis(),
-)
+data class TransitionData(val config: Config, val start: Long = System.currentTimeMillis())
 
 class KeyguardBlueprintViewModel
 @Inject
 constructor(
     @Main private val handler: Handler,
-    keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
+    private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
 ) {
     val blueprint = keyguardBlueprintInteractor.blueprint
     val blueprintId = keyguardBlueprintInteractor.blueprintId
@@ -76,6 +74,9 @@
             }
         }
 
+    fun refreshBlueprint(type: Type = Type.NoTransition) =
+        keyguardBlueprintInteractor.refreshBlueprint(type)
+
     fun updateTransitions(data: TransitionData?, mutate: MutableSet<Transition>.() -> Unit) {
         runningTransitions.mutate()
 
@@ -95,7 +96,7 @@
                 Log.w(
                     TAG,
                     "runTransition: skipping ${transition::class.simpleName}: " +
-                        "currentPriority=$currentPriority; config=$config"
+                        "currentPriority=$currentPriority; config=$config",
                 )
             }
             apply()
@@ -106,7 +107,7 @@
             Log.i(
                 TAG,
                 "runTransition: running ${transition::class.simpleName}: " +
-                    "currentPriority=$currentPriority; config=$config"
+                    "currentPriority=$currentPriority; config=$config",
             )
         }
 
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 4b62eab..0d55709 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
@@ -179,14 +179,6 @@
         } else {
             button(KeyguardQuickAffordancePosition.BOTTOM_START)
         }
-        .stateIn(
-            scope = applicationScope,
-            started = SharingStarted.Eagerly,
-            initialValue =
-                KeyguardQuickAffordanceViewModel(
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
-                ),
-        )
 
     /** An observable for the view-model of the "end button" quick affordance. */
     val endButton: Flow<KeyguardQuickAffordanceViewModel> =
@@ -200,14 +192,6 @@
         } else {
             button(KeyguardQuickAffordancePosition.BOTTOM_END)
         }
-        .stateIn(
-            scope = applicationScope,
-            started = SharingStarted.Eagerly,
-            initialValue =
-                KeyguardQuickAffordanceViewModel(
-                    slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
-                ),
-        )
 
     /**
      * Notifies that a slot with the given ID has been selected in the preview experience that is
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 38ca888..10a2e5c 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
@@ -21,7 +21,6 @@
 import android.util.MathUtils
 import android.view.View.VISIBLE
 import com.android.app.tracing.coroutines.launch
-import com.android.systemui.Flags.newAodTransition
 import com.android.systemui.common.shared.model.NotificationContainerBounds
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -29,7 +28,6 @@
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.BurnInModel
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -45,6 +43,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
@@ -58,12 +57,9 @@
 import kotlin.math.max
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
-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.combineTransform
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -86,6 +82,7 @@
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
     notificationShadeWindowModel: NotificationShadeWindowModel,
+    private val aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
     private val alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
     private val alternateBouncerToGoneTransitionViewModel:
         AlternateBouncerToGoneTransitionViewModel,
@@ -127,10 +124,6 @@
     private val aodAlphaViewModel: AodAlphaViewModel,
     private val shadeInteractor: ShadeInteractor,
 ) {
-    private var burnInJob: Job? = null
-    private val _burnInModel = MutableStateFlow(BurnInModel())
-    val burnInModel = _burnInModel.asStateFlow()
-
     val burnInLayerVisibility: Flow<Int> =
         keyguardTransitionInteractor.startedKeyguardTransitionStep
             .filter { it.to == AOD || it.to == LOCKSCREEN }
@@ -139,7 +132,7 @@
     val goneToAodTransition =
         keyguardTransitionInteractor.transition(
             edge = Edge.create(Scenes.Gone, AOD),
-            edgeWithoutSceneContainer = Edge.create(GONE, AOD)
+            edgeWithoutSceneContainer = Edge.create(GONE, AOD),
         )
 
     private val goneToAodTransitionRunning: Flow<Boolean> =
@@ -192,7 +185,7 @@
                                 /* rangeMax = */ 1f,
                                 /* valueMin = */ 0f,
                                 /* valueMax = */ 0.2f,
-                                /* value = */ max(qsExpansion, shadeExpansion)
+                                /* value = */ max(qsExpansion, shadeExpansion),
                             )
                     emit(alpha)
                 }
@@ -263,7 +256,7 @@
                         primaryBouncerToGoneTransitionViewModel.lockscreenAlpha,
                         primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
                     )
-                    .onStart { emit(1f) }
+                    .onStart { emit(1f) },
             ) { hideKeyguard, alpha ->
                 if (hideKeyguard) {
                     0f
@@ -283,30 +276,24 @@
     /** For elements that appear and move during the animation -> AOD */
     val burnInLayerAlpha: Flow<Float> = aodAlphaViewModel.alpha
 
-    val translationY: Flow<Float> = burnInModel.map { it.translationY.toFloat() }
+    val translationY: Flow<Float> = aodBurnInViewModel.movement.map { it.translationY.toFloat() }
 
     val translationX: Flow<StateToValue> =
         merge(
-            burnInModel.map { StateToValue(to = AOD, value = it.translationX.toFloat()) },
+            aodBurnInViewModel.movement.map {
+                StateToValue(to = AOD, value = it.translationX.toFloat())
+            },
             lockscreenToGlanceableHubTransitionViewModel.keyguardTranslationX,
             glanceableHubToLockscreenTransitionViewModel.keyguardTranslationX,
         )
 
     fun updateBurnInParams(params: BurnInParameters) {
-        burnInJob?.cancel()
-
-        burnInJob =
-            applicationScope.launch("$TAG#aodBurnInViewModel") {
-                aodBurnInViewModel.movement(params).collect { _burnInModel.value = it }
-            }
+        aodBurnInViewModel.updateBurnInParams(params)
     }
 
     val scale: Flow<BurnInScaleViewModel> =
-        burnInModel.map {
-            BurnInScaleViewModel(
-                scale = it.scale,
-                scaleClockOnly = it.scaleClockOnly,
-            )
+        aodBurnInViewModel.movement.map {
+            BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly)
         }
 
     /** Is the notification icon container visible? */
@@ -319,11 +306,12 @@
                     .onStart { emit(false) },
                 keyguardTransitionInteractor.isFinishedIn(
                     scene = Scenes.Gone,
-                    stateWithoutSceneContainer = GONE
+                    stateWithoutSceneContainer = GONE,
                 ),
                 deviceEntryInteractor.isBypassEnabled,
                 areNotifsFullyHiddenAnimated(),
                 isPulseExpandingAnimated(),
+                aodNotificationIconViewModel.icons.map { it.visibleIcons.isNotEmpty() },
             ) { flows ->
                 val goneToAodTransitionRunning = flows[0] as Boolean
                 val isOnLockscreen = flows[1] as Boolean
@@ -331,6 +319,7 @@
                 val isBypassEnabled = flows[3] as Boolean
                 val notifsFullyHidden = flows[4] as AnimatedValue<Boolean>
                 val pulseExpanding = flows[5] as AnimatedValue<Boolean>
+                val hasAodIcons = flows[6] as Boolean
 
                 when {
                     // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off
@@ -342,9 +331,10 @@
                     else ->
                         zip(notifsFullyHidden, pulseExpanding) {
                             areNotifsFullyHidden,
-                            isPulseExpanding,
-                            ->
+                            isPulseExpanding ->
                             when {
+                                // If there are no notification icons to show, then it can be hidden
+                                !hasAodIcons -> false
                                 // If we're bypassing, then we're visible
                                 isBypassEnabled -> true
                                 // If we are pulsing (and not bypassing), then we are hidden
@@ -396,10 +386,7 @@
                         !dozeParameters.alwaysOn -> false
                         // Don't animate when going to AOD if the display needs blanking.
                         dozeParameters.displayNeedsBlanking -> false
-                        // We only want the appear animations to happen when the notifications
-                        // get fully hidden, since otherwise the un-hide animation overlaps.
-                        newAodTransition() -> true
-                        else -> fullyHidden
+                        else -> true
                     }
                 AnimatableEvent(fullyHidden, animate)
             }
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 75e3871..c5909ed 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
@@ -107,8 +107,6 @@
                 }
             }
 
-    // TODO(b/365182034): move to interactor, add as dependency of SideFpsOverlayInteractor when
-    //  rest to unlock feature is implemented
     val isVisible: Flow<Boolean> = _visible.asStateFlow()
 
     val progress: Flow<Float> = _progress.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index c2b5d98..5559698 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -26,7 +26,7 @@
 import androidx.lifecycle.LifecycleRegistry
 import androidx.lifecycle.lifecycleScope
 import com.android.app.tracing.coroutines.createCoroutineTracingContext
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.traceCoroutine
 import com.android.systemui.Flags.coroutineTracing
 import com.android.systemui.util.Assert
 import com.android.systemui.util.Compile
@@ -45,6 +45,7 @@
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
 
 /**
  * Runs the given [block] every time the [View] becomes attached (or immediately after calling this
@@ -137,7 +138,7 @@
 ): ViewLifecycleOwner {
     return ViewLifecycleOwner(view).apply {
         onCreate()
-        lifecycleScope.launch(nameForTrace, coroutineContext) { block(view) }
+        lifecycleScope.launch(coroutineContext) { traceCoroutine(nameForTrace) { block(view) } }
     }
 }
 
@@ -367,7 +368,8 @@
  * an extension function, and plumbing dagger-injected instances for static usage has little
  * benefit.
  */
-private val MAIN_DISPATCHER_SINGLETON = Dispatchers.Main + createCoroutineTracingContext()
+private val MAIN_DISPATCHER_SINGLETON =
+    Dispatchers.Main + createCoroutineTracingContext("RepeatWhenAttached")
 private const val DEFAULT_TRACE_NAME = "repeatWhenAttached"
 private const val CURRENT_CLASS_NAME = "com.android.systemui.lifecycle.RepeatWhenAttachedKt"
 private const val JAVA_ADAPTER_CLASS_NAME = "com.android.systemui.util.kotlin.JavaAdapterKt"
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 275f1ee..39cedc3 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
@@ -128,7 +128,7 @@
         data: MediaData,
         immediately: Boolean,
         receivedSmartspaceCardLatency: Int,
-        isSsReactivated: Boolean
+        isSsReactivated: Boolean,
     ) {
         var reusedListener: PlaybackStateListener? = null
 
@@ -183,7 +183,7 @@
     override fun onSmartspaceMediaDataLoaded(
         key: String,
         data: SmartspaceMediaData,
-        shouldPrioritize: Boolean
+        shouldPrioritize: Boolean,
     ) {
         if (!mediaFlags.isPersistentSsCardEnabled()) return
 
@@ -259,7 +259,9 @@
         }
 
         override fun onPlaybackStateChanged(state: PlaybackState?) {
-            processState(state, dispatchEvents = true, currentResumption = resumption)
+            bgExecutor.execute {
+                processState(state, dispatchEvents = true, currentResumption = resumption)
+            }
         }
 
         override fun onSessionDestroyed() {
@@ -276,6 +278,7 @@
             }
         }
 
+        @WorkerThread
         private fun processState(
             state: PlaybackState?,
             dispatchEvents: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 4055818..7a6de5c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -55,6 +55,7 @@
 import com.android.systemui.media.controls.ui.viewmodel.MediaPlayerViewModel
 import com.android.systemui.media.controls.util.MediaDataUtils
 import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.surfaceeffects.ripple.MultiRippleView
@@ -66,6 +67,8 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
+private const val TAG = "MediaControlViewBinder"
+
 object MediaControlViewBinder {
 
     fun bind(
@@ -234,9 +237,6 @@
                 }
             }
             setDismissible(model.isDismissEnabled)
-            setTextPrimaryColor(model.textPrimaryColor)
-            setAccentPrimaryColor(model.accentPrimaryColor)
-            setSurfaceColor(model.surfaceColor)
         }
     }
 
@@ -423,22 +423,37 @@
         val width = viewController.widthInSceneContainerPx
         val height = viewController.heightInSceneContainerPx
         withContext(backgroundDispatcher) {
+            val wallpaperColors =
+                MediaArtworkHelper.getWallpaperColor(
+                    viewHolder.albumView.context,
+                    backgroundDispatcher,
+                    viewModel.backgroundCover,
+                    TAG,
+                )
+            val isArtworkBound = wallpaperColors != null
+            val scheme =
+                wallpaperColors?.let { ColorScheme(it, true, Style.CONTENT) }
+                    ?: let {
+                        if (viewModel.launcherIcon is Icon.Loaded) {
+                            MediaArtworkHelper.getColorScheme(viewModel.launcherIcon.drawable, TAG)
+                        } else {
+                            null
+                        }
+                    }
             val artwork =
-                if (viewModel.shouldAddGradient) {
+                wallpaperColors?.let {
                     addGradientToPlayerAlbum(
                         viewHolder.albumView.context,
                         viewModel.backgroundCover!!,
-                        viewModel.colorScheme,
+                        scheme!!,
                         width,
                         height,
                     )
-                } else {
-                    ColorDrawable(Color.TRANSPARENT)
-                }
+                } ?: ColorDrawable(Color.TRANSPARENT)
             withContext(mainDispatcher) {
                 // Transition Colors to current color scheme
                 val colorSchemeChanged =
-                    viewController.colorSchemeTransition.updateColorScheme(viewModel.colorScheme)
+                    viewController.colorSchemeTransition.updateColorScheme(scheme)
                 val albumView = viewHolder.albumView
 
                 // Set up width of album view constraint.
@@ -449,7 +464,7 @@
                 if (
                     updateBackground ||
                         colorSchemeChanged ||
-                        (!viewController.isArtworkBound && viewModel.shouldAddGradient)
+                        (!viewController.isArtworkBound && isArtworkBound)
                 ) {
                     viewController.prevArtwork?.let {
                         // Since we throw away the last transition, this will pop if your
@@ -464,12 +479,10 @@
                         transitionDrawable.isCrossFadeEnabled = true
 
                         albumView.setImageDrawable(transitionDrawable)
-                        transitionDrawable.startTransition(
-                            if (viewModel.shouldAddGradient) 333 else 80
-                        )
+                        transitionDrawable.startTransition(if (isArtworkBound) 333 else 80)
                     } ?: albumView.setImageDrawable(artwork)
                 }
-                viewController.isArtworkBound = viewModel.shouldAddGradient
+                viewController.isArtworkBound = isArtworkBound
                 viewController.prevArtwork = artwork
 
                 if (viewModel.useGrayColorFilter) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
index 5e8a879..8a04799 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
@@ -16,13 +16,24 @@
 
 package com.android.systemui.media.controls.ui.binder
 
+import android.app.WallpaperColors
 import android.content.Context
 import android.content.res.ColorStateList
 import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Color
 import android.graphics.Matrix
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.LayerDrawable
+import android.os.Trace
 import android.util.TypedValue
 import android.view.View
 import android.view.ViewGroup
+import androidx.appcompat.content.res.AppCompatResources
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
@@ -30,17 +41,29 @@
 import com.android.systemui.animation.Expandable
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
+import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
+import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.textSecondaryFromScheme
 import com.android.systemui.media.controls.ui.controller.MediaViewController
+import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
 import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
 import com.android.systemui.media.controls.ui.viewmodel.MediaRecViewModel
 import com.android.systemui.media.controls.ui.viewmodel.MediaRecommendationsViewModel
 import com.android.systemui.media.controls.ui.viewmodel.MediaRecsCardViewModel
+import com.android.systemui.monet.ColorScheme
+import com.android.systemui.monet.Style
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.util.animation.TransitionLayout
 import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val TAG = "MediaRecommendationsViewBinder"
+private const val MEDIA_REC_SCRIM_START_ALPHA = 0.15f
+private const val MEDIA_REC_SCRIM_END_ALPHA = 1.0f
 
 object MediaRecommendationsViewBinder {
 
@@ -50,6 +73,8 @@
         viewModel: MediaRecommendationsViewModel,
         mediaViewController: MediaViewController,
         falsingManager: FalsingManager,
+        backgroundDispatcher: CoroutineDispatcher,
+        mainDispatcher: CoroutineDispatcher,
     ) {
         mediaViewController.recsConfigurationChangeListener = this::updateRecommendationsVisibility
         val cardView = viewHolder.recommendations
@@ -59,7 +84,14 @@
                     launch {
                         viewModel.mediaRecsCard.collectLatest { viewModel ->
                             viewModel?.let {
-                                bindRecsCard(viewHolder, it, mediaViewController, falsingManager)
+                                bindRecsCard(
+                                    viewHolder,
+                                    it,
+                                    mediaViewController,
+                                    falsingManager,
+                                    backgroundDispatcher,
+                                    mainDispatcher,
+                                )
                             }
                         }
                     }
@@ -68,19 +100,19 @@
         }
     }
 
-    fun bindRecsCard(
+    suspend fun bindRecsCard(
         viewHolder: RecommendationViewHolder,
         viewModel: MediaRecsCardViewModel,
         viewController: MediaViewController,
         falsingManager: FalsingManager,
+        backgroundDispatcher: CoroutineDispatcher,
+        mainDispatcher: CoroutineDispatcher,
     ) {
         // Set up media control location and its listener.
         viewModel.onLocationChanged(viewController.currentEndLocation)
         viewController.locationChangeListener = viewModel.onLocationChanged
 
         // Bind main card.
-        viewHolder.cardTitle.setTextColor(viewModel.cardTitleColor)
-        viewHolder.recommendations.backgroundTintList = ColorStateList.valueOf(viewModel.cardColor)
         viewHolder.recommendations.contentDescription =
             viewModel.contentDescription.invoke(viewController.isGutsVisible)
 
@@ -100,8 +132,17 @@
             return@setOnLongClickListener true
         }
 
+        // Bind colors
+        val appIcon = viewModel.mediaRecs.first().appIcon
+        fetchAndUpdateColors(viewHolder, appIcon, backgroundDispatcher, mainDispatcher)
         // Bind all recommendations.
-        bindRecommendationsList(viewHolder, viewModel.mediaRecs, falsingManager)
+        bindRecommendationsList(
+            viewHolder,
+            viewModel.mediaRecs,
+            falsingManager,
+            backgroundDispatcher,
+            mainDispatcher,
+        )
         updateRecommendationsVisibility(viewController, viewHolder.recommendations)
 
         // Set visibility of recommendations.
@@ -153,26 +194,21 @@
         }
 
         gutsViewHolder.setDismissible(gutsViewModel.isDismissEnabled)
-        gutsViewHolder.setTextPrimaryColor(gutsViewModel.textPrimaryColor)
-        gutsViewHolder.setAccentPrimaryColor(gutsViewModel.accentPrimaryColor)
-        gutsViewHolder.setSurfaceColor(gutsViewModel.surfaceColor)
     }
 
-    private fun bindRecommendationsList(
+    private suspend fun bindRecommendationsList(
         viewHolder: RecommendationViewHolder,
         mediaRecs: List<MediaRecViewModel>,
-        falsingManager: FalsingManager
+        falsingManager: FalsingManager,
+        backgroundDispatcher: CoroutineDispatcher,
+        mainDispatcher: CoroutineDispatcher,
     ) {
         mediaRecs.forEachIndexed { index, mediaRecViewModel ->
             if (index >= NUM_REQUIRED_RECOMMENDATIONS) return@forEachIndexed
 
             val appIconView = viewHolder.mediaAppIcons[index]
             appIconView.clearColorFilter()
-            if (mediaRecViewModel.appIcon != null) {
-                appIconView.setImageDrawable(mediaRecViewModel.appIcon)
-            } else {
-                appIconView.setImageResource(R.drawable.ic_music_note)
-            }
+            appIconView.setImageDrawable(mediaRecViewModel.appIcon)
 
             val mediaCoverContainer = viewHolder.mediaCoverContainers[index]
             mediaCoverContainer.setOnClickListener {
@@ -187,29 +223,24 @@
             }
 
             val mediaCover = viewHolder.mediaCoverItems[index]
-            val width: Int =
-                mediaCover.context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
-            val height: Int =
-                mediaCover.context.resources.getDimensionPixelSize(
-                    R.dimen.qs_media_rec_album_height_expanded
-                )
-            val coverMatrix = Matrix(mediaCover.imageMatrix)
-            coverMatrix.postScale(1.25f, 1.25f, 0.5f * width, 0.5f * height)
-            mediaCover.imageMatrix = coverMatrix
-            mediaCover.setImageDrawable(mediaRecViewModel.albumIcon)
+            bindRecommendationArtwork(
+                mediaCover.context,
+                viewHolder,
+                mediaRecViewModel,
+                index,
+                backgroundDispatcher,
+                mainDispatcher,
+            )
             mediaCover.contentDescription = mediaRecViewModel.contentDescription
 
             val title = viewHolder.mediaTitles[index]
             title.text = mediaRecViewModel.title
-            title.setTextColor(ColorStateList.valueOf(mediaRecViewModel.titleColor))
 
             val subtitle = viewHolder.mediaSubtitles[index]
             subtitle.text = mediaRecViewModel.subtitle
-            subtitle.setTextColor(ColorStateList.valueOf(mediaRecViewModel.subtitleColor))
 
             val progressBar = viewHolder.mediaProgressBars[index]
             progressBar.progress = mediaRecViewModel.progress
-            progressBar.progressTintList = ColorStateList.valueOf(mediaRecViewModel.progressColor)
             if (mediaRecViewModel.progress == 0) {
                 progressBar.visibility = View.GONE
             }
@@ -291,11 +322,148 @@
                     TypedValue.applyDimension(
                             TypedValue.COMPLEX_UNIT_DIP,
                             displayAvailableDpWidth.toFloat(),
-                            res.displayMetrics
+                            res.displayMetrics,
                         )
                         .toInt()
                 displayAvailableWidth / recCoverWidth
             }
         return min(fittedNum.toDouble(), NUM_REQUIRED_RECOMMENDATIONS.toDouble()).toInt()
     }
+
+    private suspend fun bindRecommendationArtwork(
+        context: Context,
+        viewHolder: RecommendationViewHolder,
+        viewModel: MediaRecViewModel,
+        index: Int,
+        backgroundDispatcher: CoroutineDispatcher,
+        mainDispatcher: CoroutineDispatcher,
+    ) {
+        val traceCookie = viewHolder.hashCode()
+        val traceName = "MediaRecommendationsViewBinder#bindRecommendationArtwork"
+        Trace.beginAsyncSection(traceName, traceCookie)
+
+        // Capture width & height from views in foreground for artwork scaling in background
+        val width = context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
+        val height =
+            context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_height_expanded)
+
+        withContext(backgroundDispatcher) {
+            val artwork =
+                getRecCoverBackground(
+                    context,
+                    viewModel.albumIcon,
+                    width,
+                    height,
+                    backgroundDispatcher,
+                )
+            withContext(mainDispatcher) {
+                val mediaCover = viewHolder.mediaCoverItems[index]
+                val coverMatrix = Matrix(mediaCover.imageMatrix)
+                coverMatrix.postScale(1.25f, 1.25f, 0.5f * width, 0.5f * height)
+                mediaCover.imageMatrix = coverMatrix
+                mediaCover.setImageDrawable(artwork)
+            }
+        }
+    }
+
+    /** Returns the recommendation album cover of [width]x[height] size. */
+    private suspend fun getRecCoverBackground(
+        context: Context,
+        icon: Icon?,
+        width: Int,
+        height: Int,
+        backgroundDispatcher: CoroutineDispatcher,
+    ): Drawable =
+        withContext(backgroundDispatcher) {
+            return@withContext MediaArtworkHelper.getWallpaperColor(
+                    context,
+                    backgroundDispatcher,
+                    icon,
+                    TAG,
+                )
+                ?.let { wallpaperColors ->
+                    addGradientToRecommendationAlbum(
+                        context,
+                        icon!!,
+                        ColorScheme(wallpaperColors, true, Style.CONTENT),
+                        width,
+                        height,
+                    )
+                } ?: ColorDrawable(Color.TRANSPARENT)
+        }
+
+    private fun addGradientToRecommendationAlbum(
+        context: Context,
+        artworkIcon: Icon,
+        mutableColorScheme: ColorScheme,
+        width: Int,
+        height: Int,
+    ): LayerDrawable {
+        // First try scaling rec card using bitmap drawable.
+        // If returns null, set drawable bounds.
+        val albumArt =
+            getScaledRecommendationCover(context, artworkIcon, width, height)
+                ?: MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height)
+        val gradient =
+            AppCompatResources.getDrawable(context, R.drawable.qs_media_rec_scrim)?.mutate()
+                as GradientDrawable
+        return MediaArtworkHelper.setUpGradientColorOnDrawable(
+            albumArt,
+            gradient,
+            mutableColorScheme,
+            MEDIA_REC_SCRIM_START_ALPHA,
+            MEDIA_REC_SCRIM_END_ALPHA,
+        )
+    }
+
+    /** Returns a [Drawable] of a given [artworkIcon] scaled to [width]x[height] size, . */
+    private fun getScaledRecommendationCover(
+        context: Context,
+        artworkIcon: Icon,
+        width: Int,
+        height: Int,
+    ): Drawable? {
+        check(width > 0) { "Width must be a positive number but was $width" }
+        check(height > 0) { "Height must be a positive number but was $height" }
+
+        return if (
+            artworkIcon.type == Icon.TYPE_BITMAP || artworkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP
+        ) {
+            artworkIcon.bitmap?.let {
+                val bitmap = Bitmap.createScaledBitmap(it, width, height, false)
+                BitmapDrawable(context.resources, bitmap)
+            }
+        } else {
+            null
+        }
+    }
+
+    private suspend fun fetchAndUpdateColors(
+        viewHolder: RecommendationViewHolder,
+        appIcon: Drawable,
+        backgroundDispatcher: CoroutineDispatcher,
+        mainDispatcher: CoroutineDispatcher,
+    ) =
+        withContext(backgroundDispatcher) {
+            val colorScheme =
+                ColorScheme(WallpaperColors.fromDrawable(appIcon), /* darkTheme= */ true)
+            withContext(mainDispatcher) {
+                val backgroundColor = surfaceFromScheme(colorScheme)
+                val textPrimaryColor = textPrimaryFromScheme(colorScheme)
+                val textSecondaryColor = textSecondaryFromScheme(colorScheme)
+
+                viewHolder.cardTitle.setTextColor(textPrimaryColor)
+                viewHolder.recommendations.setBackgroundTintList(
+                    ColorStateList.valueOf(backgroundColor)
+                )
+
+                viewHolder.mediaTitles.forEach { it.setTextColor(textPrimaryColor) }
+                viewHolder.mediaSubtitles.forEach { it.setTextColor(textSecondaryColor) }
+                viewHolder.mediaProgressBars.forEach {
+                    it.progressTintList = ColorStateList.valueOf(textPrimaryColor)
+                }
+
+                viewHolder.gutsViewHolder.setColors(colorScheme)
+            }
+        }
 }
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 49a8758..a0fb0bf2 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
@@ -110,6 +110,7 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -174,19 +175,21 @@
      * The desired location where we'll be at the end of the transformation. Usually this matches
      * the end location, except when we're still waiting on a state update call.
      */
-    @MediaLocation private var desiredLocation: Int = -1
+    @MediaLocation private var desiredLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
 
     /**
      * The ending location of the view where it ends when all animations and transitions have
      * finished
      */
-    @MediaLocation @VisibleForTesting var currentEndLocation: Int = -1
+    @MediaLocation
+    @VisibleForTesting
+    var currentEndLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
 
     /**
      * The ending location of the view where it ends when all animations and transitions have
      * finished
      */
-    @MediaLocation private var currentStartLocation: Int = -1
+    @MediaLocation private var currentStartLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
 
     /** The progress of the transition or 1.0 if there is no transition happening */
     private var currentTransitionProgress: Float = 1.0f
@@ -726,6 +729,13 @@
                     )
                 DiffUtil.calculateDiff(diffUtilCallback).dispatchUpdatesTo(listUpdateCallback)
                 setNewViewModelsList(it)
+
+                // Update host visibility when media changes.
+                merge(
+                        mediaCarouselViewModel.hasAnyMediaOrRecommendations,
+                        mediaCarouselViewModel.hasActiveMediaOrRecommendations,
+                    )
+                    .collect { updateHostVisibility() }
             }
         }
     }
@@ -768,6 +778,8 @@
                     commonViewModel.recsViewModel,
                     viewController,
                     falsingManager,
+                    backgroundDispatcher,
+                    mainDispatcher,
                 )
                 mediaContent.addView(viewHolder.recommendations, position)
                 controllerById[commonViewModel.key] = viewController
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 8bec46a..70ca824 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
@@ -545,6 +545,7 @@
 
     /** Bind this player view based on the data given. */
     public void bindPlayer(@NonNull MediaData data, String key) {
+        SceneContainerFlag.assertInLegacyMode();
         if (mMediaViewHolder == null) {
             return;
         }
@@ -638,10 +639,7 @@
         // to something which might impact the measurement
         // State refresh interferes with the translation animation, only run it if it's not running.
         if (!mMetadataAnimationHandler.isRunning()) {
-            // Don't refresh in scene framework, because it will calculate with invalid layout sizes
-            if (!SceneContainerFlag.isEnabled()) {
-                mMediaViewController.refreshState();
-            }
+            mMediaViewController.refreshState();
         }
 
         if (shouldPlayTurbulenceNoise()) {
@@ -907,11 +905,6 @@
         // Capture width & height from views in foreground for artwork scaling in background
         int width = mMediaViewHolder.getAlbumView().getMeasuredWidth();
         int height = mMediaViewHolder.getAlbumView().getMeasuredHeight();
-        if (SceneContainerFlag.isEnabled() && (width <= 0 || height <= 0)) {
-            // TODO(b/312714128): ensure we have a valid size before setting background
-            width = mMediaViewController.getWidthInSceneContainerPx();
-            height = mMediaViewController.getHeightInSceneContainerPx();
-        }
 
         final int finalWidth = width;
         final int finalHeight = height;
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 38cea5b..745ab12 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
@@ -64,7 +64,6 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -161,10 +160,10 @@
     private var animationStartAlpha = 0.0f
 
     /** The starting location of the cross fade if an animation is running right now. */
-    @MediaLocation private var crossFadeAnimationStartLocation = -1
+    @MediaLocation private var crossFadeAnimationStartLocation = LOCATION_UNKNOWN
 
     /** The end location of the cross fade if an animation is running right now. */
-    @MediaLocation private var crossFadeAnimationEndLocation = -1
+    @MediaLocation private var crossFadeAnimationEndLocation = LOCATION_UNKNOWN
     private var targetBounds: Rect = Rect()
     private val mediaFrame
         get() = mediaCarouselController.mediaFrame
@@ -191,7 +190,7 @@
                     animationStartBounds,
                     targetBounds,
                     boundsProgress,
-                    result = currentBounds
+                    result = currentBounds,
                 )
                 resolveClipping(currentClipping)
                 applyState(currentBounds, currentAlpha, clipBounds = currentClipping)
@@ -233,16 +232,16 @@
      * The last location where this view was at before going to the desired location. This is useful
      * for guided transitions.
      */
-    @MediaLocation private var previousLocation = -1
+    @MediaLocation private var previousLocation = LOCATION_UNKNOWN
     /** The desired location where the view will be at the end of the transition. */
-    @MediaLocation private var desiredLocation = -1
+    @MediaLocation private var desiredLocation = LOCATION_UNKNOWN
 
     /**
      * The current attachment location where the view is currently attached. Usually this matches
      * the desired location except for animations whenever a view moves to the new desired location,
      * during which it is in [IN_OVERLAY].
      */
-    @MediaLocation private var currentAttachmentLocation = -1
+    @MediaLocation private var currentAttachmentLocation = LOCATION_UNKNOWN
 
     private var inSplitShade = false
 
@@ -627,7 +626,7 @@
                             secureSettings.getBoolForUser(
                                 Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
                                 true,
-                                UserHandle.USER_CURRENT
+                                UserHandle.USER_CURRENT,
                             )
                     }
                 }
@@ -635,7 +634,7 @@
         secureSettings.registerContentObserverForUserSync(
             Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
             settingsObserver,
-            UserHandle.USER_ALL
+            UserHandle.USER_ALL,
         )
 
         // Listen to the communal UI state. Make sure that communal UI is showing and hub itself is
@@ -651,7 +650,7 @@
                     shadeInteractor.shadeExpansion
                         .mapLatest { it < EXPANSION_THRESHOLD }
                         .distinctUntilChanged(),
-                    ::Triple
+                    ::Triple,
                 )
                 .collectLatest { (communalShowing, isDreaming, isShadeExpanding) ->
                     isCommunalShowing = communalShowing
@@ -689,10 +688,10 @@
         if (mediaObject.location == desiredLocation) {
             // In case we are overriding a view that is already visible, make sure we attach it
             // to this new host view in the below call
-            desiredLocation = -1
+            desiredLocation = LOCATION_UNKNOWN
         }
         if (mediaObject.location == currentAttachmentLocation) {
-            currentAttachmentLocation = -1
+            currentAttachmentLocation = LOCATION_UNKNOWN
         }
         updateDesiredLocation()
         return viewHost
@@ -734,7 +733,7 @@
      */
     private fun updateDesiredLocation(
         forceNoAnimation: Boolean = false,
-        forceStateUpdate: Boolean = false
+        forceStateUpdate: Boolean = false,
     ) =
         traceSection("MediaHierarchyManager#updateDesiredLocation") {
             val desiredLocation = calculateLocation()
@@ -758,7 +757,7 @@
                         previousLocation = LOCATION_QQS
                     }
                 }
-                val isNewView = this.desiredLocation == -1
+                val isNewView = this.desiredLocation == LOCATION_UNKNOWN
                 this.desiredLocation = desiredLocation
                 // Let's perform a transition
                 val animate =
@@ -774,7 +773,7 @@
                         host,
                         animate,
                         animDuration,
-                        delay
+                        delay,
                     )
                 }
                 performTransitionToNewLocation(isNewView, animate)
@@ -868,7 +867,7 @@
 
     private fun shouldAnimateTransition(
         @MediaLocation currentLocation: Int,
-        @MediaLocation previousLocation: Int
+        @MediaLocation previousLocation: Int,
     ): Boolean {
         if (isCurrentlyInGuidedTransformation()) {
             return false
@@ -990,7 +989,7 @@
         startBounds: Rect,
         endBounds: Rect,
         progress: Float,
-        result: Rect? = null
+        result: Rect? = null,
     ): Rect {
         val left =
             MathUtils.lerp(startBounds.left.toFloat(), endBounds.left.toFloat(), progress).toInt()
@@ -1014,7 +1013,7 @@
     }
 
     private fun hasValidStartAndEndLocations(): Boolean {
-        return previousLocation != -1 && desiredLocation != -1
+        return previousLocation != LOCATION_UNKNOWN && desiredLocation != LOCATION_UNKNOWN
     }
 
     /** Calculate the transformation type for the current animation */
@@ -1099,21 +1098,21 @@
         bounds: Rect,
         alpha: Float,
         immediately: Boolean = false,
-        clipBounds: Rect = EMPTY_RECT
+        clipBounds: Rect = EMPTY_RECT,
     ) =
         traceSection("MediaHierarchyManager#applyState") {
             currentBounds.set(bounds)
             currentClipping = clipBounds
             carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f
             val onlyUseEndState = !isCurrentlyInGuidedTransformation() || isCurrentlyFading()
-            val startLocation = if (onlyUseEndState) -1 else previousLocation
+            val startLocation = if (onlyUseEndState) LOCATION_UNKNOWN else previousLocation
             val progress = if (onlyUseEndState) 1.0f else getTransformationProgress()
             val endLocation = resolveLocationForFading()
             mediaCarouselController.setCurrentState(
                 startLocation,
                 endLocation,
                 progress,
-                immediately
+                immediately,
             )
             updateHostAttachment()
             if (currentAttachmentLocation == IN_OVERLAY) {
@@ -1125,7 +1124,7 @@
                     currentBounds.left,
                     currentBounds.top,
                     currentBounds.right,
-                    currentBounds.bottom
+                    currentBounds.bottom,
                 )
             }
         }
@@ -1186,7 +1185,7 @@
                     mediaCarouselController.onDesiredLocationChanged(
                         newLocation,
                         getHost(newLocation),
-                        animate = false
+                        animate = false,
                     )
                 }
             }
@@ -1201,7 +1200,7 @@
         if (isCrossFadeAnimatorRunning) {
             // When animating between two hosts with a fade, let's keep ourselves in the old
             // location for the first half, and then switch over to the end location
-            if (animationCrossFadeProgress > 0.5 || previousLocation == -1) {
+            if (animationCrossFadeProgress > 0.5 || previousLocation == LOCATION_UNKNOWN) {
                 return crossFadeAnimationEndLocation
             } else {
                 return crossFadeAnimationStartLocation
@@ -1364,6 +1363,9 @@
         /** Attached at the root of the hierarchy in an overlay */
         const val IN_OVERLAY = -1000
 
+        /** Not attached to any view */
+        const val LOCATION_UNKNOWN = -1
+
         /**
          * The default transformation type where the hosts transform into each other using a direct
          * transition
@@ -1388,8 +1390,8 @@
     value =
         [
             MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION,
-            MediaHierarchyManager.TRANSFORMATION_TYPE_FADE
-        ]
+            MediaHierarchyManager.TRANSFORMATION_TYPE_FADE,
+        ],
 )
 @Retention(AnnotationRetention.SOURCE)
 private annotation class TransformationType
@@ -1403,7 +1405,8 @@
             MediaHierarchyManager.LOCATION_LOCKSCREEN,
             MediaHierarchyManager.LOCATION_DREAM_OVERLAY,
             MediaHierarchyManager.LOCATION_COMMUNAL_HUB,
-        ]
+            MediaHierarchyManager.LOCATION_UNKNOWN,
+        ],
 )
 @Retention(AnnotationRetention.SOURCE)
 annotation class MediaLocation
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 e57de09..3928a71 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
@@ -91,7 +91,7 @@
      */
     enum class TYPE {
         PLAYER,
-        RECOMMENDATION
+        RECOMMENDATION,
     }
 
     companion object {
@@ -120,7 +120,7 @@
      * finished
      */
     @MediaLocation
-    var currentEndLocation: Int = -1
+    var currentEndLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
         set(value) {
             if (field != value) {
                 field = value
@@ -130,7 +130,7 @@
         }
 
     /** The starting location of the view where it starts for all animations and transitions */
-    @MediaLocation private var currentStartLocation: Int = -1
+    @MediaLocation private var currentStartLocation: Int = MediaHierarchyManager.LOCATION_UNKNOWN
 
     /** The progress of the transition or 1.0 if there is no transition happening */
     private var currentTransitionProgress: Float = 1.0f
@@ -294,14 +294,14 @@
         object : MediaHostStatesManager.Callback {
             override fun onHostStateChanged(
                 @MediaLocation location: Int,
-                mediaHostState: MediaHostState
+                mediaHostState: MediaHostState,
             ) {
                 if (location == currentEndLocation || location == currentStartLocation) {
                     setCurrentState(
                         currentStartLocation,
                         currentEndLocation,
                         currentTransitionProgress,
-                        applyImmediately = false
+                        applyImmediately = false,
                     )
                 }
             }
@@ -442,7 +442,7 @@
     /** Apply squishFraction to a copy of viewState such that the cached version is untouched. */
     internal fun squishViewState(
         viewState: TransitionViewState,
-        squishFraction: Float
+        squishFraction: Float,
     ): TransitionViewState {
         val squishedViewState = viewState.copy()
         val squishedHeight = (squishedViewState.measureHeight * squishFraction).toInt()
@@ -458,13 +458,13 @@
             MediaViewHolder.expandedBottomActionIds,
             squishedViewState.measureHeight.toFloat(),
             squishedViewState,
-            squishFraction
+            squishFraction,
         )
         calculateWidgetGroupAlphaForSquishiness(
             MediaViewHolder.detailIds,
             squishedViewState.measureHeight.toFloat(),
             squishedViewState,
-            squishFraction
+            squishFraction,
         )
         // recommendation card
         val titlesTop =
@@ -472,13 +472,13 @@
                 RecommendationViewHolder.mediaTitlesAndSubtitlesIds,
                 squishedViewState.measureHeight.toFloat(),
                 squishedViewState,
-                squishFraction
+                squishFraction,
             )
         calculateWidgetGroupAlphaForSquishiness(
             RecommendationViewHolder.mediaContainersIds,
             titlesTop,
             squishedViewState,
-            squishFraction
+            squishFraction,
         )
         return squishedViewState
     }
@@ -517,7 +517,7 @@
         widgetGroupIds: Set<Int>,
         groupEndPosition: Float,
         squishedViewState: TransitionViewState,
-        squishFraction: Float
+        squishFraction: Float,
     ): Float {
         val nonsquishedHeight = squishedViewState.measureHeight
         var groupTop = squishedViewState.measureHeight.toFloat()
@@ -547,7 +547,7 @@
                         calculateAlpha(
                             squishFraction,
                             startPosition / nonsquishedHeight,
-                            endPosition / nonsquishedHeight
+                            endPosition / nonsquishedHeight,
                         )
                 }
             }
@@ -562,10 +562,10 @@
     @VisibleForTesting
     fun obtainViewState(
         state: MediaHostState?,
-        isGutsAnimation: Boolean = false
+        isGutsAnimation: Boolean = false,
     ): TransitionViewState? {
         if (SceneContainerFlag.isEnabled) {
-            return obtainSceneContainerViewState()
+            return obtainSceneContainerViewState(state)
         }
 
         if (state == null || state.measurementInput == null) {
@@ -606,7 +606,7 @@
                 transitionLayout!!.calculateViewState(
                     state.measurementInput!!,
                     constraintSetForExpansion(state.expansion),
-                    TransitionViewState()
+                    TransitionViewState(),
                 )
 
             setGutsViewState(result)
@@ -653,7 +653,7 @@
             logger.logMediaLocation("attach $type", currentStartLocation, currentEndLocation)
             this.transitionLayout = transitionLayout
             layoutController.attach(transitionLayout)
-            if (currentEndLocation == -1) {
+            if (currentEndLocation == MediaHierarchyManager.LOCATION_UNKNOWN) {
                 return
             }
             // Set the previously set state immediately to the view, now that it's finally attached
@@ -661,7 +661,7 @@
                 startLocation = currentStartLocation,
                 endLocation = currentEndLocation,
                 transitionProgress = currentTransitionProgress,
-                applyImmediately = true
+                applyImmediately = true,
             )
         }
 
@@ -695,7 +695,7 @@
                 Interpolators.EMPHASIZED_DECELERATE,
                 titleText,
                 artistText,
-                explicitIndicator
+                explicitIndicator,
             )
         val exit =
             loadAnimator(
@@ -704,7 +704,7 @@
                 Interpolators.EMPHASIZED_ACCELERATE,
                 titleText,
                 artistText,
-                explicitIndicator
+                explicitIndicator,
             )
         metadataAnimationHandler = MetadataAnimationHandler(exit, enter)
 
@@ -713,7 +713,7 @@
                 mediaCard.context,
                 mediaViewHolder,
                 multiRippleController,
-                turbulenceNoiseController
+                turbulenceNoiseController,
             )
 
         // For Turbulence noise.
@@ -728,7 +728,7 @@
             object : LoadingEffect.AnimationStateChangedCallback {
                 override fun onStateChanged(
                     oldState: LoadingEffect.AnimationState,
-                    newState: LoadingEffect.AnimationState
+                    newState: LoadingEffect.AnimationState,
                 ) {
                     if (newState === LoadingEffect.AnimationState.NOT_PLAYING) {
                         loadingEffectView.visibility = View.INVISIBLE
@@ -755,12 +755,12 @@
                 MediaControlViewBinder.setVisibleAndAlpha(
                     expandedLayout,
                     it.scrubbingTotalTimeView.id,
-                    isTimeVisible
+                    isTimeVisible,
                 )
                 MediaControlViewBinder.setVisibleAndAlpha(
                     expandedLayout,
                     it.scrubbingElapsedTimeView.id,
-                    isTimeVisible
+                    isTimeVisible,
                 )
             }
 
@@ -788,7 +788,7 @@
                         collapsedLayout,
                         isButtonVisible,
                         notVisibleValue,
-                        showInCollapsed = true
+                        showInCollapsed = true,
                     )
                 }
             }
@@ -822,7 +822,7 @@
                     createTurbulenceNoiseConfig(
                         it.loadingEffectView,
                         it.turbulenceNoiseView,
-                        colorSchemeTransition
+                        colorSchemeTransition,
                     )
             }
             if (Flags.shaderlibLoadingEffectRefactor()) {
@@ -832,23 +832,23 @@
                             TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
                             turbulenceNoiseAnimationConfig,
                             noiseDrawCallback,
-                            stateChangedCallback
+                            stateChangedCallback,
                         )
                 }
                 colorSchemeTransition.loadingEffect = loadingEffect
                 loadingEffect.play()
                 mainExecutor.executeDelayed(
                     loadingEffect::finish,
-                    MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+                    MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION,
                 )
             } else {
                 turbulenceNoiseController.play(
                     TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
-                    turbulenceNoiseAnimationConfig
+                    turbulenceNoiseAnimationConfig,
                 )
                 mainExecutor.executeDelayed(
                     turbulenceNoiseController::finish,
-                    MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+                    MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION,
                 )
             }
         }
@@ -920,7 +920,7 @@
                             startViewState,
                             startHostState.disappearParameters,
                             transitionProgress,
-                            tmpState
+                            tmpState,
                         )
                 }
             } else if (startHostState != null && !startHostState.visible) {
@@ -931,7 +931,7 @@
                         endViewState,
                         endHostState.disappearParameters,
                         1.0f - transitionProgress,
-                        tmpState
+                        tmpState,
                     )
             } else if (transitionProgress == 1.0f || startViewState == null) {
                 // We're at the end. Let's use that state
@@ -945,13 +945,13 @@
                         startViewState,
                         endViewState,
                         transitionProgress,
-                        tmpState
+                        tmpState,
                     )
             }
             logger.logMediaSize(
                 "setCurrentState (progress $transitionProgress)",
                 result.width,
-                result.height
+                result.height,
             )
             layoutController.setState(
                 result,
@@ -966,7 +966,7 @@
     private fun updateViewStateSize(
         viewState: TransitionViewState?,
         location: Int,
-        outState: TransitionViewState
+        outState: TransitionViewState,
     ): TransitionViewState? {
         var result = viewState?.copy(outState) ?: return null
         val state = mediaHostStatesManager.mediaHostStates[location]
@@ -1020,15 +1020,19 @@
     }
 
     /** Get a view state based on the width and height set by the scene */
-    private fun obtainSceneContainerViewState(): TransitionViewState? {
+    private fun obtainSceneContainerViewState(state: MediaHostState?): TransitionViewState? {
         logger.logMediaSize("scene container", widthInSceneContainerPx, heightInSceneContainerPx)
 
+        if (state?.measurementInput == null) {
+            return null
+        }
+
         // Similar to obtainViewState: Let's create a new measurement
         val result =
             transitionLayout?.calculateViewState(
                 MeasurementInput(widthInSceneContainerPx, heightInSceneContainerPx),
-                expandedLayout,
-                TransitionViewState()
+                if (state.expansion > 0) expandedLayout else collapsedLayout,
+                TransitionViewState(),
             )
         result?.let {
             // And then ensure the guts visibility is set correctly
@@ -1049,7 +1053,7 @@
     private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? {
         val mediaHostState = mediaHostStatesManager.mediaHostStates[location] ?: return null
         if (SceneContainerFlag.isEnabled) {
-            return obtainSceneContainerViewState()
+            return obtainSceneContainerViewState(mediaHostState)
         }
 
         val viewState = obtainViewState(mediaHostState)
@@ -1080,9 +1084,10 @@
     fun refreshState() =
         traceSection("MediaViewController#refreshState") {
             if (SceneContainerFlag.isEnabled) {
+                val hostState = mediaHostStatesManager.mediaHostStates[currentEndLocation]
                 // We don't need to recreate measurements for scene container, since it's a known
                 // size. Just get the view state and update the layout controller
-                obtainSceneContainerViewState()?.let {
+                obtainSceneContainerViewState(hostState)?.let {
                     // Get scene container state, then setCurrentState
                     layoutController.setState(
                         state = it,
@@ -1106,7 +1111,7 @@
                 currentStartLocation,
                 currentEndLocation,
                 currentTransitionProgress,
-                applyImmediately = true
+                applyImmediately = true,
             )
         }
 
@@ -1115,7 +1120,7 @@
         context: Context,
         animId: Int,
         motionInterpolator: Interpolator?,
-        vararg targets: View?
+        vararg targets: View?,
     ): AnimatorSet {
         val animators = ArrayList<Animator>()
         for (target in targets) {
@@ -1132,7 +1137,7 @@
     private fun createTurbulenceNoiseConfig(
         loadingEffectView: LoadingEffectView,
         turbulenceNoiseView: TurbulenceNoiseView,
-        colorSchemeTransition: ColorSchemeTransition
+        colorSchemeTransition: ColorSchemeTransition,
     ): TurbulenceNoiseAnimationConfig {
         val targetView: View =
             if (Flags.shaderlibLoadingEffectRefactor()) {
@@ -1163,7 +1168,7 @@
             targetView.context.resources.displayMetrics.density,
             lumaMatteBlendFactor = 0.26f,
             lumaMatteOverallBrightness = 0.09f,
-            shouldInverseNoiseLuminosity = false
+            shouldInverseNoiseLuminosity = false,
         )
     }
 
@@ -1185,5 +1190,5 @@
     var widthMeasureSpec: Int = -1,
     var heightMeasureSpec: Int = -1,
     var expansion: Float = 0.0f,
-    var gutsVisible: Boolean = false
+    var gutsVisible: Boolean = false,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
index c97221e..c21513b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaArtworkHelper.kt
@@ -87,26 +87,19 @@
         gradient: GradientDrawable,
         colorScheme: ColorScheme,
         startAlpha: Float,
-        endAlpha: Float
+        endAlpha: Float,
     ): LayerDrawable {
         gradient.colors =
             intArrayOf(
                 getColorWithAlpha(backgroundStartFromScheme(colorScheme), startAlpha),
-                getColorWithAlpha(backgroundEndFromScheme(colorScheme), endAlpha)
+                getColorWithAlpha(backgroundEndFromScheme(colorScheme), endAlpha),
             )
         return LayerDrawable(arrayOf(albumArt, gradient))
     }
 
-    /** Returns [ColorScheme] of media app given its [packageName]. */
-    fun getColorScheme(
-        applicationContext: Context,
-        packageName: String,
-        tag: String,
-        style: Style = Style.TONAL_SPOT
-    ): ColorScheme? {
+    /** Returns [ColorScheme] of media app given its [icon]. */
+    fun getColorScheme(icon: Drawable, tag: String, style: Style = Style.TONAL_SPOT): ColorScheme? {
         return try {
-            // Set up media source app's logo.
-            val icon = applicationContext.packageManager.getApplicationIcon(packageName)
             ColorScheme(WallpaperColors.fromDrawable(icon), true, style)
         } catch (e: PackageManager.NameNotFoundException) {
             Log.w(tag, "Fail to get media app info", e)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt
index 6c7c31c..314d9af 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/GutsViewModel.kt
@@ -16,15 +16,11 @@
 
 package com.android.systemui.media.controls.ui.viewmodel
 
-import android.annotation.ColorInt
 import android.graphics.drawable.Drawable
 
 /** Models UI state for media guts menu */
 data class GutsViewModel(
     val gutsText: CharSequence,
-    @ColorInt val textPrimaryColor: Int,
-    @ColorInt val accentPrimaryColor: Int,
-    @ColorInt val surfaceColor: Int,
     val isDismissEnabled: Boolean = true,
     val onDismissClicked: () -> Unit,
     val cancelTextBackground: Drawable?,
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 e7f7171..b2137af 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
@@ -56,6 +56,9 @@
     private val mediaLogger: MediaLogger,
 ) {
 
+    val hasAnyMediaOrRecommendations: StateFlow<Boolean> = interactor.hasAnyMediaOrRecommendation
+    val hasActiveMediaOrRecommendations: StateFlow<Boolean> =
+        interactor.hasActiveMediaOrRecommendation
     val mediaItems: StateFlow<List<MediaCommonViewModel>> =
         interactor.currentMedia
             .map { sortedItems ->
@@ -114,7 +117,7 @@
         qsExpanded: Boolean,
         visibleIndex: Int,
         location: Int,
-        isUpdate: Boolean = false
+        isUpdate: Boolean = false,
     ) {
         // Skip logging if on LS or QQS, and there is no active media card
         if (!qsExpanded && !interactor.hasActiveMediaOrRecommendation()) return
@@ -127,7 +130,7 @@
         val instanceId = commonModel.mediaLoadedModel.instanceId
         return mediaControlByInstanceId[instanceId]?.copy(
             immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
-            updateTime = commonModel.updateTime
+            updateTime = commonModel.updateTime,
         )
             ?: MediaCommonViewModel.MediaControl(
                     instanceId = instanceId,
@@ -144,7 +147,7 @@
                     },
                     onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) },
                     isMediaFromRec = commonModel.isMediaFromRec,
-                    updateTime = commonModel.updateTime
+                    updateTime = commonModel.updateTime,
                 )
                 .also { mediaControlByInstanceId[instanceId] = it }
     }
@@ -165,7 +168,7 @@
         return mediaRecs?.copy(
             key = commonModel.recsLoadingModel.key,
             loadingEnabled =
-                interactor.isRecommendationActive() || mediaFlags.isPersistentSsCardEnabled()
+                interactor.isRecommendationActive() || mediaFlags.isPersistentSsCardEnabled(),
         )
             ?: MediaCommonViewModel.MediaRecommendations(
                     key = commonModel.recsLoadingModel.key,
@@ -195,7 +198,7 @@
 
     private fun onMediaControlAddedOrUpdated(
         commonViewModel: MediaCommonViewModel,
-        commonModel: MediaCommonModel.MediaControl
+        commonModel: MediaCommonModel.MediaControl,
     ) {
         if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) {
             // This media control is due for removal as it is now paused + timed out, and resumption
@@ -222,7 +225,7 @@
 
     private fun onMediaRecommendationRemoved(
         commonModel: MediaCommonModel.MediaRecommendations,
-        immediatelyRemove: Boolean
+        immediatelyRemove: Boolean,
     ) {
         mediaLogger.logMediaRecommendationCardRemoved(commonModel.recsLoadingModel.key)
         if (immediatelyRemove || isReorderingAllowed()) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 104d155..4173d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -33,15 +33,11 @@
 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.MediaControlModel
-import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
-import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
-import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
-import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
 import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
 import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
 import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.monet.ColorScheme
-import com.android.systemui.monet.Style
 import com.android.systemui.res.R
 import java.util.concurrent.Executor
 import kotlinx.coroutines.CoroutineDispatcher
@@ -76,7 +72,7 @@
 
     private var isPlaying = false
     private var isAnyButtonClicked = false
-    private var location = -1
+    @MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN
     private var playerViewModel: MediaPlayerViewModel? = null
 
     fun isNewPlayer(viewModel: MediaPlayerViewModel): Boolean {
@@ -104,26 +100,9 @@
         )
     }
 
-    private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? {
+    private fun toViewModel(model: MediaControlModel): MediaPlayerViewModel {
         val mediaController = model.token?.let { MediaController(applicationContext, it) }
-        val wallpaperColors =
-            MediaArtworkHelper.getWallpaperColor(
-                applicationContext,
-                backgroundDispatcher,
-                model.artwork,
-                TAG,
-            )
-        val scheme =
-            wallpaperColors?.let { ColorScheme(it, true, Style.CONTENT) }
-                ?: MediaArtworkHelper.getColorScheme(
-                    applicationContext,
-                    model.packageName,
-                    TAG,
-                    Style.CONTENT,
-                )
-                ?: return null
-
-        val gutsViewModel = toGutsViewModel(model, scheme)
+        val gutsViewModel = toGutsViewModel(model)
 
         // Set playing state
         val wasPlaying = isPlaying
@@ -154,8 +133,6 @@
             artistName = model.artistName ?: "",
             titleName = model.songName ?: "",
             isExplicitVisible = model.showExplicit,
-            shouldAddGradient = wallpaperColors != null,
-            colorScheme = scheme,
             canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons),
             playTurbulenceNoise = isPlaying && !wasPlaying && wasButtonClicked,
             useSemanticActions = model.semanticActionButtons != null,
@@ -276,7 +253,7 @@
         )
     }
 
-    private fun toGutsViewModel(model: MediaControlModel, scheme: ColorScheme): GutsViewModel {
+    private fun toGutsViewModel(model: MediaControlModel): GutsViewModel {
         return GutsViewModel(
             gutsText =
                 if (model.isDismissible) {
@@ -287,9 +264,6 @@
                 } else {
                     applicationContext.getString(R.string.controls_media_active_session)
                 },
-            textPrimaryColor = textPrimaryFromScheme(scheme),
-            accentPrimaryColor = accentPrimaryFromScheme(scheme),
-            surfaceColor = surfaceFromScheme(scheme),
             isDismissEnabled = model.isDismissible,
             onDismissClicked = {
                 onDismissMediaData(model.token, model.uid, model.packageName, model.instanceId)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index f4b0d6e..4aae72f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.monet.ColorScheme
 
 /** Models UI state for media player. */
 data class MediaPlayerViewModel(
@@ -30,8 +29,6 @@
     val artistName: CharSequence,
     val titleName: CharSequence,
     val isExplicitVisible: Boolean,
-    val shouldAddGradient: Boolean,
-    val colorScheme: ColorScheme,
     val canShowTime: Boolean,
     val playTurbulenceNoise: Boolean,
     val useSemanticActions: Boolean,
@@ -52,7 +49,6 @@
                 artistName == other.artistName &&
                 titleName == other.titleName &&
                 isExplicitVisible == other.isExplicitVisible &&
-                shouldAddGradient == other.shouldAddGradient &&
                 canShowTime == other.canShowTime &&
                 playTurbulenceNoise == other.playTurbulenceNoise &&
                 useSemanticActions == other.useSemanticActions &&
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt
index 2f9fc9b..77add40 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecViewModel.kt
@@ -16,21 +16,18 @@
 
 package com.android.systemui.media.controls.ui.viewmodel
 
-import android.annotation.ColorInt
 import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
 import com.android.systemui.animation.Expandable
 
 /** Models UI state for media recommendation item */
 data class MediaRecViewModel(
     val contentDescription: CharSequence,
     val title: CharSequence = "",
-    @ColorInt val titleColor: Int,
     val subtitle: CharSequence = "",
-    @ColorInt val subtitleColor: Int,
     /** track progress [0 - 100] for the recommendation album. */
     val progress: Int = 0,
-    @ColorInt val progressColor: Int,
-    val albumIcon: Drawable? = null,
-    val appIcon: Drawable? = null,
+    val albumIcon: Icon? = null,
+    val appIcon: Drawable,
     val onClicked: ((Expandable, Int) -> Unit),
 )
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
index 1fd9c4f0..6bc6b10a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
@@ -18,17 +18,10 @@
 
 import android.content.Context
 import android.content.Intent
-import android.graphics.Bitmap
-import android.graphics.Color
-import android.graphics.drawable.BitmapDrawable
-import android.graphics.drawable.ColorDrawable
+import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
-import android.graphics.drawable.GradientDrawable
-import android.graphics.drawable.Icon
-import android.graphics.drawable.LayerDrawable
 import android.os.Process
 import android.util.Log
-import androidx.appcompat.content.res.AppCompatResources
 import com.android.internal.logging.InstanceId
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.SysUISingleton
@@ -38,18 +31,13 @@
 import com.android.systemui.media.controls.shared.model.MediaRecModel
 import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
 import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
-import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
-import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
-import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
-import com.android.systemui.media.controls.ui.animation.textSecondaryFromScheme
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
 import com.android.systemui.media.controls.ui.controller.MediaViewController.Companion.GUTS_ANIMATION_DURATION
-import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
 import com.android.systemui.media.controls.util.MediaDataUtils
 import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
 import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
 import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.monet.ColorScheme
-import com.android.systemui.monet.Style
 import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -59,7 +47,6 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.withContext
 
 /** Models UI state and handles user input for media recommendations */
 @SysUISingleton
@@ -81,7 +68,7 @@
             .distinctUntilChanged()
             .flowOn(backgroundDispatcher)
 
-    private var location = -1
+    @MediaLocation private var location = MediaHierarchyManager.LOCATION_UNKNOWN
 
     /**
      * Called whenever the recommendation has been expired or removed by the user. This method
@@ -92,7 +79,7 @@
         uid: Int,
         packageName: String,
         dismissIntent: Intent?,
-        instanceId: InstanceId?
+        instanceId: InstanceId?,
     ) {
         logger.logLongPressDismiss(uid, packageName, instanceId)
         interactor.removeMediaRecommendations(
@@ -100,7 +87,7 @@
             dismissIntent,
             GUTS_DISMISS_DELAY_MS_DURATION,
             SMARTSPACE_CARD_DISMISS_EVENT,
-            location
+            location,
         )
     }
 
@@ -109,7 +96,7 @@
         intent: Intent?,
         packageName: String,
         instanceId: InstanceId?,
-        index: Int
+        index: Int,
     ) {
         if (intent == null || intent.extras == null) {
             Log.e(TAG, "No tap action can be set up")
@@ -131,7 +118,7 @@
             SMARTSPACE_CARD_CLICK_EVENT,
             location,
             index,
-            NUM_REQUIRED_RECOMMENDATIONS
+            NUM_REQUIRED_RECOMMENDATIONS,
         )
     }
 
@@ -145,22 +132,7 @@
             return null
         }
 
-        val scheme =
-            MediaArtworkHelper.getColorScheme(applicationContext, model.packageName, TAG)
-                ?: return null
-
-        // Capture width & height from views in foreground for artwork scaling in background
-        val width =
-            applicationContext.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
-        val height =
-            applicationContext.resources.getDimensionPixelSize(
-                R.dimen.qs_media_rec_album_height_expanded
-            )
-
-        val appIcon = applicationContext.packageManager.getApplicationIcon(model.packageName)
-        val textPrimaryColor = textPrimaryFromScheme(scheme)
-        val textSecondaryColor = textSecondaryFromScheme(scheme)
-        val backgroundColor = surfaceFromScheme(scheme)
+        val appIcon = getIconFromApp(model.packageName) ?: return null
 
         var areTitlesVisible = false
         var areSubtitlesVisible = false
@@ -173,17 +145,9 @@
                     contentDescription =
                         setUpMediaRecContentDescription(mediaRecModel, model.appName),
                     title = mediaRecModel.title ?: "",
-                    titleColor = textPrimaryColor,
                     subtitle = mediaRecModel.subtitle ?: "",
-                    subtitleColor = textSecondaryColor,
                     progress = (progress * 100).toInt(),
-                    progressColor = textPrimaryColor,
-                    albumIcon =
-                        getRecCoverBackground(
-                            mediaRecModel.icon,
-                            width,
-                            height,
-                        ),
+                    albumIcon = mediaRecModel.icon,
                     appIcon = appIcon,
                     onClicked = { expandable, index ->
                         onClicked(
@@ -193,7 +157,7 @@
                             model.instanceId,
                             index,
                         )
-                    }
+                    },
                 )
             }
         // Subtitles should only be visible if titles are visible.
@@ -204,21 +168,19 @@
                 if (gutsVisible) {
                     applicationContext.getString(
                         R.string.controls_media_close_session,
-                        model.appName
+                        model.appName,
                     )
                 } else {
                     applicationContext.getString(R.string.controls_media_smartspace_rec_header)
                 }
             },
-            cardColor = backgroundColor,
-            cardTitleColor = textPrimaryColor,
             onClicked = { expandable ->
                 onClicked(
                     expandable,
                     model.dismissIntent,
                     model.packageName,
                     model.instanceId,
-                    index = -1
+                    index = -1,
                 )
             },
             onLongClicked = {
@@ -227,28 +189,22 @@
             mediaRecs = mediaRecs,
             areTitlesVisible = areTitlesVisible,
             areSubtitlesVisible = areSubtitlesVisible,
-            gutsMenu = toGutsViewModel(model, scheme),
-            onLocationChanged = { location = it }
+            gutsMenu = toGutsViewModel(model),
+            onLocationChanged = { location = it },
         )
     }
 
-    private fun toGutsViewModel(
-        model: MediaRecommendationsModel,
-        scheme: ColorScheme
-    ): GutsViewModel {
+    private fun toGutsViewModel(model: MediaRecommendationsModel): GutsViewModel {
         return GutsViewModel(
             gutsText =
                 applicationContext.getString(R.string.controls_media_close_session, model.appName),
-            textPrimaryColor = textPrimaryFromScheme(scheme),
-            accentPrimaryColor = accentPrimaryFromScheme(scheme),
-            surfaceColor = surfaceFromScheme(scheme),
             onDismissClicked = {
                 onMediaRecommendationsDismissed(
                     model.key,
                     model.uid,
                     model.packageName,
                     model.dismissIntent,
-                    model.instanceId
+                    model.instanceId,
                 )
             },
             cancelTextBackground =
@@ -260,56 +216,9 @@
         )
     }
 
-    /** Returns the recommendation album cover of [width]x[height] size. */
-    private suspend fun getRecCoverBackground(icon: Icon?, width: Int, height: Int): Drawable =
-        withContext(backgroundDispatcher) {
-            return@withContext MediaArtworkHelper.getWallpaperColor(
-                    applicationContext,
-                    backgroundDispatcher,
-                    icon,
-                    TAG,
-                )
-                ?.let { wallpaperColors ->
-                    addGradientToRecommendationAlbum(
-                        icon!!,
-                        ColorScheme(wallpaperColors, true, Style.CONTENT),
-                        width,
-                        height
-                    )
-                } ?: ColorDrawable(Color.TRANSPARENT)
-        }
-
-    private fun addGradientToRecommendationAlbum(
-        artworkIcon: Icon,
-        mutableColorScheme: ColorScheme,
-        width: Int,
-        height: Int
-    ): LayerDrawable {
-        // First try scaling rec card using bitmap drawable.
-        // If returns null, set drawable bounds.
-        val albumArt =
-            getScaledRecommendationCover(artworkIcon, width, height)
-                ?: MediaArtworkHelper.getScaledBackground(
-                    applicationContext,
-                    artworkIcon,
-                    width,
-                    height
-                )
-        val gradient =
-            AppCompatResources.getDrawable(applicationContext, R.drawable.qs_media_rec_scrim)
-                ?.mutate() as GradientDrawable
-        return MediaArtworkHelper.setUpGradientColorOnDrawable(
-            albumArt,
-            gradient,
-            mutableColorScheme,
-            MEDIA_REC_SCRIM_START_ALPHA,
-            MEDIA_REC_SCRIM_END_ALPHA
-        )
-    }
-
     private fun setUpMediaRecContentDescription(
         mediaRec: MediaRecModel,
-        appName: CharSequence?
+        appName: CharSequence?,
     ): CharSequence {
         // Set up the accessibility label for the media item.
         val artistName = mediaRec.extras?.getString(KEY_SMARTSPACE_ARTIST_NAME, "")
@@ -317,35 +226,23 @@
             applicationContext.getString(
                 R.string.controls_media_smartspace_rec_item_no_artist_description,
                 mediaRec.title,
-                appName
+                appName,
             )
         } else {
             applicationContext.getString(
                 R.string.controls_media_smartspace_rec_item_description,
                 mediaRec.title,
                 artistName,
-                appName
+                appName,
             )
         }
     }
 
-    /** Returns a [Drawable] of a given [artworkIcon] scaled to [width]x[height] size, . */
-    private fun getScaledRecommendationCover(
-        artworkIcon: Icon,
-        width: Int,
-        height: Int
-    ): Drawable? {
-        check(width > 0) { "Width must be a positive number but was $width" }
-        check(height > 0) { "Height must be a positive number but was $height" }
-
-        return if (
-            artworkIcon.type == Icon.TYPE_BITMAP || artworkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP
-        ) {
-            artworkIcon.bitmap?.let {
-                val bitmap = Bitmap.createScaledBitmap(it, width, height, false)
-                BitmapDrawable(applicationContext.resources, bitmap)
-            }
-        } else {
+    private fun getIconFromApp(packageName: String): Drawable? {
+        return try {
+            applicationContext.packageManager.getApplicationIcon(packageName)
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.w(TAG, "Cannot find icon for package $packageName", e)
             null
         }
     }
@@ -353,8 +250,6 @@
     companion object {
         private const val TAG = "MediaRecommendationsViewModel"
         private const val KEY_SMARTSPACE_ARTIST_NAME = "artist_name"
-        private const val MEDIA_REC_SCRIM_START_ALPHA = 0.15f
-        private const val MEDIA_REC_SCRIM_END_ALPHA = 1.0f
         /**
          * Delay duration is based on [GUTS_ANIMATION_DURATION], it should have 100 ms increase in
          * order to let the animation end.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
index 5ecbcb2..f1f7dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
@@ -16,14 +16,11 @@
 
 package com.android.systemui.media.controls.ui.viewmodel
 
-import android.annotation.ColorInt
 import com.android.systemui.animation.Expandable
 
 /** Models UI state for media recommendations card. */
 data class MediaRecsCardViewModel(
     val contentDescription: (Boolean) -> CharSequence,
-    @ColorInt val cardColor: Int,
-    @ColorInt val cardTitleColor: Int,
     val onClicked: (Expandable) -> Unit,
     val onLongClicked: () -> Unit,
     val mediaRecs: List<MediaRecViewModel>,
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 f7b7353..b69b25d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -77,6 +77,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.media.InfoMediaManager;
+import com.android.settingslib.media.InputMediaDevice;
 import com.android.settingslib.media.InputRouteManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
@@ -875,6 +876,17 @@
     }
 
     protected void connectDevice(MediaDevice device) {
+        // If input routing is supported and the device is an input device, call mInputRouteManager
+        // to handle routing.
+        if (enableInputRouting() && device instanceof InputMediaDevice) {
+            var unused =
+                    ThreadUtils.postOnBackgroundThread(
+                            () -> {
+                                mInputRouteManager.selectDevice(device);
+                            });
+            return;
+        }
+
         mMetricLogger.updateOutputEndPoints(getCurrentConnectedMediaDevice(), device);
 
         ThreadUtils.postOnBackgroundThread(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index 228b576..d413474 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -54,6 +54,7 @@
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
 import com.android.systemui.res.R
+import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.AsyncActivityLauncher
 import java.lang.IllegalArgumentException
@@ -62,9 +63,10 @@
 class MediaProjectionAppSelectorActivity(
     private val componentFactory: MediaProjectionAppSelectorComponent.Factory,
     private val activityLauncher: AsyncActivityLauncher,
+    private val activityManager: ActivityManagerWrapper,
     /** This is used to override the dependency in a screenshot test */
     @VisibleForTesting
-    private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)?
+    private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)?,
 ) :
     ChooserActivity(),
     MediaProjectionAppSelectorView,
@@ -74,8 +76,9 @@
     @Inject
     constructor(
         componentFactory: MediaProjectionAppSelectorComponent.Factory,
-        activityLauncher: AsyncActivityLauncher
-    ) : this(componentFactory, activityLauncher, listControllerFactory = null)
+        activityLauncher: AsyncActivityLauncher,
+        activityManager: ActivityManagerWrapper,
+    ) : this(componentFactory, activityLauncher, activityManager, listControllerFactory = null)
 
     private val lifecycleRegistry = LifecycleRegistry(this)
     override val lifecycle = lifecycleRegistry
@@ -100,7 +103,7 @@
                 callingPackage = callingPackage,
                 view = this,
                 resultHandler = this,
-                isFirstStart = savedInstanceState == null
+                isFirstStart = savedInstanceState == null,
             )
         component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
 
@@ -113,7 +116,7 @@
         intent.configureChooserIntent(
             resources,
             component.hostUserHandle,
-            component.personalProfileUserHandle
+            component.personalProfileUserHandle,
         )
 
         reviewGrantedConsentRequired =
@@ -180,7 +183,13 @@
         // is created and ready to be captured.
         val activityStarted =
             activityLauncher.startActivityAsUser(intent, userHandle, activityOptions.toBundle()) {
-                returnSelectedApp(launchCookie, taskId = -1)
+                if (targetInfo.resolvedComponentName == callingActivity) {
+                    // If attempting to launch the app used to launch the MediaProjection, then
+                    // provide the task id since the launch cookie won't match the existing task
+                    returnSelectedApp(launchCookie, taskId = activityManager.runningTask.taskId)
+                } else {
+                    returnSelectedApp(launchCookie, taskId = -1)
+                }
             }
 
         // Rely on the ActivityManager to pop up a dialog regarding app suspension
@@ -213,7 +222,7 @@
             MediaProjectionServiceHelper.setReviewedConsentIfNeeded(
                 RECORD_CANCEL,
                 reviewGrantedConsentRequired,
-                /* projection= */ null
+                /* projection= */ null,
             )
             if (isFinishing) {
                 // Only log dismissed when actually finishing, and not when changing configuration.
@@ -246,7 +255,7 @@
             val resultReceiver =
                 intent.getParcelableExtra(
                     EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
-                    ResultReceiver::class.java
+                    ResultReceiver::class.java,
                 ) as ResultReceiver
             val captureRegion = MediaProjectionCaptureTarget(launchCookie, taskId)
             val data = Bundle().apply { putParcelable(KEY_CAPTURE_TARGET, captureRegion) }
@@ -260,8 +269,8 @@
             val mediaProjectionBinder = intent.getIBinderExtra(EXTRA_MEDIA_PROJECTION)
             val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder)
 
-            projection.setLaunchCookie(launchCookie)
-            projection.setTaskId(taskId)
+            projection.launchCookie = launchCookie
+            projection.taskId = taskId
 
             val intent = Intent()
             intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder())
@@ -270,7 +279,7 @@
             MediaProjectionServiceHelper.setReviewedConsentIfNeeded(
                 RECORD_CONTENT_TASK,
                 reviewGrantedConsentRequired,
-                projection
+                projection,
             )
         }
 
@@ -457,7 +466,7 @@
      */
     private class RecyclerViewExpandingAccessibilityDelegate(
         rdl: ResolverDrawerLayout,
-        view: RecyclerView
+        view: RecyclerView,
     ) : RecyclerViewAccessibilityDelegate(view) {
 
         private val delegate = AppListAccessibilityDelegate(rdl)
@@ -465,7 +474,7 @@
         override fun onRequestSendAccessibilityEvent(
             host: ViewGroup,
             child: View,
-            event: AccessibilityEvent
+            event: AccessibilityEvent,
         ): Boolean {
             super.onRequestSendAccessibilityEvent(host, child, event)
             return delegate.onRequestSendAccessibilityEvent(host, child, event)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 6440205..544dbdd 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.mediaprojection.appselector
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.app.Activity
 import android.content.ComponentName
 import android.content.Context
@@ -133,7 +134,7 @@
         @MediaProjectionAppSelector
         @MediaProjectionAppSelectorScope
         fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
-            CoroutineScope(applicationScope.coroutineContext + SupervisorJob())
+            CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("MediaProjectionAppSelectorScope"))
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index b3c697e..1216a88 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -551,6 +551,12 @@
     }
 
     @Override
+    public void appTransitionStarting(int displayId, long startTime, long duration,
+            boolean forced) {
+        appTransitionPending(false);
+    }
+
+    @Override
     public void appTransitionCancelled(int displayId) {
         appTransitionPending(false);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 44460ed..eff5fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -75,7 +75,9 @@
      * [NoteTaskController], ensure custom actions can be triggered (i.e., keyboard shortcut).
      */
     private fun initializeHandleSystemKey() {
-        commandQueue.addCallback(callbacks)
+        if (!useKeyGestureEventHandler()) {
+            commandQueue.addCallback(callbacks)
+        }
     }
 
     /**
@@ -130,6 +132,11 @@
             InputManager.KeyGestureEventHandler {
 
             override fun handleSystemKey(key: KeyEvent) {
+                if (useKeyGestureEventHandler()) {
+                    throw IllegalStateException(
+                        "handleSystemKey must not be used when KeyGestureEventHandler is used"
+                    )
+                }
                 key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask)
             }
 
@@ -151,13 +158,13 @@
 
             override fun handleKeyGestureEvent(
                 event: KeyGestureEvent,
-                focusedToken: IBinder?
+                focusedToken: IBinder?,
             ): Boolean {
                 return this@NoteTaskInitializer.handleKeyGestureEvent(event)
             }
 
             override fun isKeyGestureSupported(gestureType: Int): Boolean {
-                return this@NoteTaskInitializer.isKeyGestureSupported(gestureType);
+                return this@NoteTaskInitializer.isKeyGestureSupported(gestureType)
             }
         }
 
@@ -209,8 +216,20 @@
             "handleKeyGestureEvent: Received OPEN_NOTES gesture event from keycodes: " +
                 event.keycodes.contentToString()
         }
-        backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) }
-        return true
+        if (
+            event.keycodes.contains(KEYCODE_N) &&
+                event.hasModifiers(KeyEvent.META_CTRL_ON or KeyEvent.META_META_ON)
+        ) {
+            debugLog { "Note task triggered by keyboard shortcut" }
+            backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) }
+            return true
+        }
+        if (event.keycodes.size == 1 && event.keycodes[0] == KEYCODE_STYLUS_BUTTON_TAIL) {
+            debugLog { "Note task triggered by stylus tail button" }
+            backgroundExecutor.execute { controller.showNoteTask(TAIL_BUTTON) }
+            return true
+        }
+        return false
     }
 
     private fun isKeyGestureSupported(gestureType: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
index 5be225c..219e45c 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
@@ -16,8 +16,7 @@
 
 package com.android.systemui.notifications.ui.viewmodel
 
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import dagger.assisted.AssistedFactory
@@ -34,13 +33,10 @@
 constructor(
     val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
     val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
-    private val sceneInteractor: SceneInteractor,
+    private val shadeInteractor: ShadeInteractor,
 ) {
     fun onScrimClicked() {
-        sceneInteractor.hideOverlay(
-            overlay = Overlays.NotificationsShade,
-            loggingReason = "Shade scrim clicked",
-        )
+        shadeInteractor.collapseNotificationsShade(loggingReason = "Shade scrim clicked")
     }
 
     @AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 13cedc2..6cf5b32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -107,6 +107,8 @@
         setLayoutForMediaInScene();
     };
 
+    private boolean mLastListening;
+
     @VisibleForTesting
     protected final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             new QSPanel.OnConfigurationChangedListener() {
@@ -242,6 +244,8 @@
         switchTileLayout(true);
 
         mDumpManager.registerDumpable(mView.getDumpableTag(), this);
+
+        setListening(mLastListening);
     }
 
     private void registerForMediaInteractorChanges() {
@@ -259,7 +263,10 @@
         mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag());
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
 
+        // Call directly so mLastListening is not modified. We want that to have the last actual
+        // value.
         mView.getTileLayout().setListening(false, mUiEventLogger);
+        mView.setListening(false);
 
         mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener);
 
@@ -436,6 +443,7 @@
     }
 
     void setListening(boolean listening) {
+        mLastListening = listening;
         if (mView.isListening() == listening) return;
         mView.setListening(listening);
 
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 af167d4..ba0d938 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -17,17 +17,24 @@
 package com.android.systemui.qs.composefragment
 
 import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.PointF
 import android.graphics.Rect
 import android.os.Bundle
 import android.util.IndentingPrintWriter
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
+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.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
@@ -38,10 +45,14 @@
 import androidx.compose.foundation.layout.windowInsetsPadding
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.onPlaced
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.positionInRoot
@@ -51,11 +62,18 @@
 import androidx.compose.ui.semantics.CustomAccessibilityAction
 import androidx.compose.ui.semantics.customActions
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.transitions
 import com.android.compose.modifiers.height
 import com.android.compose.modifiers.padding
 import com.android.compose.modifiers.thenIf
@@ -70,13 +88,20 @@
 import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
+import com.android.systemui.qs.composefragment.SceneKeys.QuickSettings
+import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
 import com.android.systemui.qs.composefragment.ui.notificationScrimClip
+import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
 import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
 import com.android.systemui.qs.flags.QSComposeFragment
 import com.android.systemui.qs.footer.ui.compose.FooterActions
+import com.android.systemui.qs.panels.ui.compose.EditMode
 import com.android.systemui.qs.panels.ui.compose.QuickQuickSettings
+import com.android.systemui.qs.shared.ui.ElementKeys
+import com.android.systemui.qs.ui.composable.QuickSettingsLayout
+import com.android.systemui.qs.ui.composable.QuickSettingsShade
 import com.android.systemui.qs.ui.composable.QuickSettingsTheme
-import com.android.systemui.qs.ui.composable.ShadeBody
 import com.android.systemui.res.R
 import com.android.systemui.util.LifecycleFragment
 import com.android.systemui.util.asIndenting
@@ -86,11 +111,13 @@
 import java.util.function.Consumer
 import javax.inject.Inject
 import javax.inject.Named
+import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
 @SuppressLint("ValidFragment")
@@ -161,42 +188,105 @@
         savedInstanceState: Bundle?,
     ): View {
         val context = inflater.context
-        return ComposeView(context).apply {
-            setBackPressedDispatcher()
-            setContent {
-                PlatformTheme {
-                    val visible by viewModel.qsVisible.collectAsStateWithLifecycle()
-                    val qsState by viewModel.expansionState.collectAsStateWithLifecycle()
+        val composeView =
+            ComposeView(context).apply {
+                setBackPressedDispatcher()
+                setContent {
+                    PlatformTheme {
+                        val visible by viewModel.qsVisible.collectAsStateWithLifecycle()
 
-                    AnimatedVisibility(
-                        visible = visible,
-                        modifier =
-                            Modifier.windowInsetsPadding(WindowInsets.navigationBars).thenIf(
-                                notificationScrimClippingParams.isEnabled
-                            ) {
-                                Modifier.notificationScrimClip(
-                                    notificationScrimClippingParams.leftInset,
-                                    notificationScrimClippingParams.top,
-                                    notificationScrimClippingParams.rightInset,
-                                    notificationScrimClippingParams.bottom,
-                                    notificationScrimClippingParams.radius,
-                                )
-                            },
-                    ) {
-                        AnimatedContent(targetState = qsState) {
-                            when (it) {
-                                QSFragmentComposeViewModel.QSExpansionState.QQS -> {
-                                    QuickQuickSettingsElement()
+                        AnimatedVisibility(
+                            visible = visible,
+                            modifier =
+                                Modifier.windowInsetsPadding(WindowInsets.navigationBars)
+                                    .thenIf(notificationScrimClippingParams.isEnabled) {
+                                        Modifier.notificationScrimClip(
+                                            notificationScrimClippingParams.leftInset,
+                                            notificationScrimClippingParams.top,
+                                            notificationScrimClippingParams.rightInset,
+                                            notificationScrimClippingParams.bottom,
+                                            notificationScrimClippingParams.radius,
+                                        )
+                                    }
+                                    .graphicsLayer { elevation = 4.dp.toPx() },
+                        ) {
+                            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 by
+                                        viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+                                    EditMode(
+                                        viewModel = viewModel.containerViewModel.editModeViewModel,
+                                        modifier =
+                                            Modifier.fillMaxWidth()
+                                                .padding(top = { qqsPadding })
+                                                .padding(
+                                                    horizontal = {
+                                                        QuickSettingsShade.Dimensions.Padding
+                                                            .roundToPx()
+                                                    }
+                                                ),
+                                    )
+                                } else {
+                                    CollapsableQuickSettingsSTL()
                                 }
-                                QSFragmentComposeViewModel.QSExpansionState.QS -> {
-                                    QuickSettingsElement()
-                                }
-                                else -> {}
                             }
                         }
                     }
                 }
             }
+
+        val frame =
+            FrameLayoutTouchPassthrough(
+                context,
+                { notificationScrimClippingParams.isEnabled },
+                { notificationScrimClippingParams.top },
+            )
+        frame.addView(
+            composeView,
+            FrameLayout.LayoutParams.MATCH_PARENT,
+            FrameLayout.LayoutParams.MATCH_PARENT,
+        )
+        return frame
+    }
+
+    /**
+     * STL that contains both QQS (tiles) and QS (brightness, tiles, footer actions), but no Edit
+     * mode. It tracks [QSFragmentComposeViewModel.expansionState] to drive the transition between
+     * [SceneKeys.QuickQuickSettings] and [SceneKeys.QuickSettings].
+     */
+    @Composable
+    private fun CollapsableQuickSettingsSTL() {
+        val sceneState = remember {
+            MutableSceneTransitionLayoutState(
+                viewModel.expansionState.value.toIdleSceneKey(),
+                transitions =
+                    transitions {
+                        from(QuickQuickSettings, QuickSettings) {
+                            quickQuickSettingsToQuickSettings()
+                        }
+                    },
+            )
+        }
+
+        LaunchedEffect(Unit) {
+            synchronizeQsState(sceneState, viewModel.expansionState.map { it.progress })
+        }
+
+        SceneTransitionLayout(state = sceneState, modifier = Modifier.fillMaxSize()) {
+            scene(QuickSettings) { QuickSettingsElement() }
+
+            scene(QuickQuickSettings) { QuickQuickSettingsElement() }
         }
     }
 
@@ -412,7 +502,7 @@
                         qsContainerController,
                         viewModel.containerViewModel.editModeViewModel.isEditing,
                     ) {
-                        setCustomizerShowing(it)
+                        setCustomizerShowing(it, EDIT_MODE_TIME_MILLIS.toLong())
                     }
                 }
             }
@@ -420,7 +510,7 @@
     }
 
     @Composable
-    private fun QuickQuickSettingsElement() {
+    private fun SceneScope.QuickQuickSettingsElement() {
         val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
         val bottomPadding = dimensionResource(id = R.dimen.qqs_layout_padding_bottom)
         DisposableEffect(Unit) {
@@ -450,8 +540,15 @@
                         viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel,
                         modifier =
                             Modifier.collapseExpandSemanticAction(
-                                stringResource(id = R.string.accessibility_quick_settings_expand)
-                            ),
+                                    stringResource(
+                                        id = R.string.accessibility_quick_settings_expand
+                                    )
+                                )
+                                .padding(
+                                    horizontal = {
+                                        QuickSettingsShade.Dimensions.Padding.roundToPx()
+                                    }
+                                ),
                     )
                 }
             }
@@ -460,7 +557,7 @@
     }
 
     @Composable
-    private fun QuickSettingsElement() {
+    private fun SceneScope.QuickSettingsElement() {
         val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
         val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top)
         Column(
@@ -471,19 +568,27 @@
         ) {
             val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
             if (qsEnabled) {
-                Box(modifier = Modifier.fillMaxSize().weight(1f)) {
+                Box(
+                    modifier =
+                        Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f)
+                ) {
                     Column {
                         Spacer(
                             modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
                         )
-                        ShadeBody(viewModel = viewModel.containerViewModel)
+                        QuickSettingsLayout(
+                            viewModel = viewModel.containerViewModel,
+                            modifier = Modifier.sysuiResTag("quick_settings_panel"),
+                        )
                     }
                 }
                 QuickSettingsTheme {
                     FooterActions(
                         viewModel = viewModel.footerActionsViewModel,
                         qsVisibilityLifecycleOwner = this@QSFragmentCompose,
-                        modifier = Modifier.sysuiResTag("qs_footer_actions"),
+                        modifier =
+                            Modifier.sysuiResTag("qs_footer_actions")
+                                .element(ElementKeys.FooterActions),
                     )
                 }
             }
@@ -590,3 +695,110 @@
             return currentId++
         }
     }
+
+object SceneKeys {
+    val QuickQuickSettings = SceneKey("QuickQuickSettingsScene")
+    val QuickSettings = SceneKey("QuickSettingsScene")
+
+    fun QSFragmentComposeViewModel.QSExpansionState.toIdleSceneKey(): SceneKey {
+        return when {
+            progress < 0.5f -> QuickQuickSettings
+            else -> QuickSettings
+        }
+    }
+}
+
+suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansion: Flow<Float>) {
+    coroutineScope {
+        val animationScope = this
+
+        var currentTransition: ExpansionTransition? = null
+
+        fun snapTo(scene: SceneKey) {
+            state.snapToScene(scene)
+            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
+                    }
+
+                    val newTransition =
+                        ExpansionTransition(progress).also { currentTransition = it }
+                    state.startTransitionImmediately(
+                        animationScope = animationScope,
+                        transition = newTransition,
+                    )
+                }
+            }
+        }
+    }
+}
+
+private class ExpansionTransition(currentProgress: Float) :
+    TransitionState.Transition.ChangeScene(
+        fromScene = QuickQuickSettings,
+        toScene = QuickSettings,
+    ) {
+    override val currentScene: SceneKey
+        get() {
+            // This should return the logical scene. If the QS STLState is only driven by
+            // synchronizeQSState() then it probably does not matter which one we return, this is
+            // only used to compute the current user actions of a STL.
+            return QuickQuickSettings
+        }
+
+    override var progress: Float by mutableFloatStateOf(currentProgress)
+
+    override val progressVelocity: Float
+        get() = 0f
+
+    override val isInitiatedByUserInput: Boolean
+        get() = true
+
+    override val isUserInputOngoing: Boolean
+        get() = true
+
+    private val finishCompletable = CompletableDeferred<Unit>()
+
+    override suspend fun run() {
+        // This transition runs until it is interrupted by another one.
+        finishCompletable.await()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        finishCompletable.complete(Unit)
+    }
+}
+
+private const val EDIT_MODE_TIME_MILLIS = 500
+
+/**
+ * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as
+ * per [clippingEnabledProvider].
+ */
+private class FrameLayoutTouchPassthrough(
+    context: Context,
+    private val clippingEnabledProvider: () -> Boolean,
+    private val clippingTopProvider: () -> Int,
+) : FrameLayout(context) {
+    override fun isTransformedTouchPointInView(
+        x: Float,
+        y: Float,
+        child: View?,
+        outLocalPoint: PointF?,
+    ): Boolean {
+        return if (clippingEnabledProvider() && y + translationY > clippingTopProvider()) {
+            false
+        } else {
+            super.isTransformedTouchPointInView(x, y, child, outLocalPoint)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
new file mode 100644
index 0000000..1514986
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/FromQuickQuickSettingsToQuickSettings.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.qs.composefragment.ui
+
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.qs.shared.ui.ElementKeys
+
+fun TransitionBuilder.quickQuickSettingsToQuickSettings() {
+
+    fractionRange(start = 0.5f) { fade(ElementKeys.QuickSettingsContent) }
+
+    fractionRange(start = 0.9f) { fade(ElementKeys.FooterActions) }
+
+    anchoredTranslate(ElementKeys.QuickSettingsContent, ElementKeys.GridAnchor)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/GridAnchor.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/GridAnchor.kt
new file mode 100644
index 0000000..f0f46d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/GridAnchor.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.qs.composefragment.ui
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.qs.shared.ui.ElementKeys
+
+/**
+ * This composable is used at the start of the tiles in QQS and QS to anchor the expansion and be
+ * able to have relative anchor translation of elements that appear in QS.
+ */
+@Composable
+fun SceneScope.GridAnchor(modifier: Modifier = Modifier) {
+    // The size of this anchor does not matter, as the tiles don't change size on expansion.
+    Spacer(modifier.element(ElementKeys.GridAnchor))
+}
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 7ab11d2..7300ee1 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
@@ -147,7 +147,7 @@
             .stateIn(
                 lifecycleScope,
                 SharingStarted.WhileSubscribed(),
-                disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled()
+                disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled(),
             )
 
     private val _showCollapsedOnKeyguard = MutableStateFlow(false)
@@ -213,19 +213,11 @@
         }
 
     val expansionState: StateFlow<QSExpansionState> =
-        combine(
-                _stackScrollerOverscrolling,
-                _qsExpanded,
-                _qsExpansion,
-            ) { args: Array<Any> ->
+        combine(_stackScrollerOverscrolling, _qsExpanded, _qsExpansion) { args: Array<Any> ->
                 val expansion = args[2] as Float
-                if (expansion > 0.5f) {
-                    QSExpansionState.QS
-                } else {
-                    QSExpansionState.QQS
-                }
+                QSExpansionState(expansion.coerceIn(0f, 1f))
             }
-            .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), QSExpansionState.QQS)
+            .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), QSExpansionState(0f))
 
     /**
      * Accessibility action for collapsing/expanding QS. The provided runnable is responsible for
@@ -262,13 +254,6 @@
         fun create(lifecycleScope: LifecycleCoroutineScope): QSFragmentComposeViewModel
     }
 
-    sealed interface QSExpansionState {
-        data object QQS : QSExpansionState
-
-        data object QS : QSExpansionState
-
-        @JvmInline value class Expanding(val progress: Float) : QSExpansionState
-
-        @JvmInline value class Collapsing(val progress: Float) : QSExpansionState
-    }
+    // In the future, this will have other relevant elements like squishiness.
+    data class QSExpansionState(@FloatRange(0.0, 1.0) val progress: Float)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 278352c..ead38f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.log.dagger.QSConfigLog
 import com.android.systemui.log.dagger.QSLog
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.QSTile.State
 import com.android.systemui.statusbar.StatusBarState
 import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
@@ -57,6 +58,7 @@
     fun d(@CompileTimeConstant msg: String, arg: Any) {
         buffer.log(TAG, DEBUG, { str1 = arg.toString() }, { "$msg: $str1" })
     }
+
     fun i(@CompileTimeConstant msg: String, arg: Any) {
         buffer.log(TAG, INFO, { str1 = arg.toString() }, { "$msg: $str1" })
     }
@@ -73,7 +75,19 @@
                 str1 = tileSpec
                 str2 = reason
             },
-            { "[$str1] Tile destroyed. Reason: $str2" }
+            { "[$str1] Tile destroyed. Reason: $str2" },
+        )
+    }
+
+    fun logStateChanged(tileSpec: String, state: State) {
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = tileSpec
+                str2 = state.toString()
+            },
+            { "[$str1] Tile state=$str2" },
         )
     }
 
@@ -85,7 +99,7 @@
                 bool1 = listening
                 str1 = tileSpec
             },
-            { "[$str1] Tile listening=$bool1" }
+            { "[$str1] Tile listening=$bool1" },
         )
     }
 
@@ -98,7 +112,7 @@
                 str1 = containerName
                 str2 = allSpecs
             },
-            { "Tiles listening=$bool1 in $str1. $str2" }
+            { "Tiles listening=$bool1 in $str1. $str2" },
         )
     }
 
@@ -112,7 +126,7 @@
                 str2 = StatusBarState.toString(statusBarState)
                 str3 = toStateString(state)
             },
-            { "[$str1][$int1] Tile clicked. StatusBarState=$str2. TileState=$str3" }
+            { "[$str1][$int1] Tile clicked. StatusBarState=$str2. TileState=$str3" },
         )
     }
 
@@ -124,7 +138,7 @@
                 str1 = tileSpec
                 int1 = eventId
             },
-            { "[$str1][$int1] Tile handling click." }
+            { "[$str1][$int1] Tile handling click." },
         )
     }
 
@@ -138,7 +152,7 @@
                 str2 = StatusBarState.toString(statusBarState)
                 str3 = toStateString(state)
             },
-            { "[$str1][$int1] Tile secondary clicked. StatusBarState=$str2. TileState=$str3" }
+            { "[$str1][$int1] Tile secondary clicked. StatusBarState=$str2. TileState=$str3" },
         )
     }
 
@@ -150,7 +164,7 @@
                 str1 = tileSpec
                 int1 = eventId
             },
-            { "[$str1][$int1] Tile handling secondary click." }
+            { "[$str1][$int1] Tile handling secondary click." },
         )
     }
 
@@ -164,7 +178,7 @@
                 str2 = StatusBarState.toString(statusBarState)
                 str3 = toStateString(state)
             },
-            { "[$str1][$int1] Tile long clicked. StatusBarState=$str2. TileState=$str3" }
+            { "[$str1][$int1] Tile long clicked. StatusBarState=$str2. TileState=$str3" },
         )
     }
 
@@ -176,7 +190,7 @@
                 str1 = tileSpec
                 int1 = eventId
             },
-            { "[$str1][$int1] Tile handling long click." }
+            { "[$str1][$int1] Tile handling long click." },
         )
     }
 
@@ -189,7 +203,7 @@
                 int1 = lastType
                 str2 = callback
             },
-            { "[$str1] mLastTileState=$int1, Callback=$str2." }
+            { "[$str1] mLastTileState=$int1, Callback=$str2." },
         )
     }
 
@@ -198,7 +212,7 @@
         tileSpec: String,
         state: Int,
         disabledByPolicy: Boolean,
-        color: Int
+        color: Int,
     ) {
         // This method is added to further debug b/250618218 which has only been observed from the
         // InternetTile, so we are only logging the background color change for the InternetTile
@@ -215,7 +229,7 @@
                 bool1 = disabledByPolicy
                 int2 = color
             },
-            { "[$str1] state=$int1, disabledByPolicy=$bool1, color=$int2." }
+            { "[$str1] state=$int1, disabledByPolicy=$bool1, color=$int2." },
         )
     }
 
@@ -229,7 +243,7 @@
                 str3 = state.icon?.toString()
                 int1 = state.state
             },
-            { "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." }
+            { "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." },
         )
     }
 
@@ -241,7 +255,7 @@
                 str1 = containerName
                 bool1 = expanded
             },
-            { "$str1 expanded=$bool1" }
+            { "$str1 expanded=$bool1" },
         )
     }
 
@@ -253,7 +267,7 @@
                 str1 = containerName
                 int1 = orientation
             },
-            { "onViewAttached: $str1 orientation $int1" }
+            { "onViewAttached: $str1 orientation $int1" },
         )
     }
 
@@ -265,7 +279,7 @@
                 str1 = containerName
                 int1 = orientation
             },
-            { "onViewDetached: $str1 orientation $int1" }
+            { "onViewDetached: $str1 orientation $int1" },
         )
     }
 
@@ -276,7 +290,7 @@
         newShouldUseSplitShade: Boolean,
         oldScreenLayout: Int,
         newScreenLayout: Int,
-        containerName: String
+        containerName: String,
     ) {
         configChangedBuffer.log(
             TAG,
@@ -297,7 +311,7 @@
                     "screen layout=${toScreenLayoutString(long1.toInt())} " +
                     "(was ${toScreenLayoutString(long2.toInt())}), " +
                     "splitShade=$bool2 (was $bool1)"
-            }
+            },
         )
     }
 
@@ -305,7 +319,7 @@
         after: Boolean,
         before: Boolean,
         force: Boolean,
-        containerName: String
+        containerName: String,
     ) {
         buffer.log(
             TAG,
@@ -316,7 +330,7 @@
                 bool2 = before
                 bool3 = force
             },
-            { "change tile layout: $str1 horizontal=$bool1 (was $bool2), force? $bool3" }
+            { "change tile layout: $str1 horizontal=$bool1 (was $bool2), force? $bool3" },
         )
     }
 
@@ -328,7 +342,7 @@
                 int1 = tilesPerPageCount
                 int2 = totalTilesCount
             },
-            { "Distributing tiles: [tilesPerPageCount=$int1] [totalTilesCount=$int2]" }
+            { "Distributing tiles: [tilesPerPageCount=$int1] [totalTilesCount=$int2]" },
         )
     }
 
@@ -340,7 +354,7 @@
                 str1 = tileName
                 int1 = pageIndex
             },
-            { "Adding $str1 to page number $int1" }
+            { "Adding $str1 to page number $int1" },
         )
     }
 
@@ -361,7 +375,7 @@
                 str1 = viewName
                 str2 = toVisibilityString(visibility)
             },
-            { "$str1 visibility: $str2" }
+            { "$str1 visibility: $str2" },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index 02a607d..fc59a50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -40,7 +40,7 @@
     private val currentTilesInteractor: CurrentTilesInteractor,
     private val preferencesInteractor: QSPreferencesInteractor,
     @PanelsLog private val logBuffer: LogBuffer,
-    @Application private val applicationScope: CoroutineScope
+    @Application private val applicationScope: CoroutineScope,
 ) {
 
     val largeTilesSpecs =
@@ -64,14 +64,15 @@
 
     fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec)
 
-    fun resize(spec: TileSpec) {
+    fun resize(spec: TileSpec, toIcon: Boolean) {
         if (!isCurrent(spec)) {
             return
         }
 
-        if (largeTilesSpecs.value.contains(spec)) {
+        val isIcon = !largeTilesSpecs.value.contains(spec)
+        if (toIcon && !isIcon) {
             preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value - spec)
-        } else {
+        } else if (!toIcon && isIcon) {
             preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value + spec)
         }
     }
@@ -85,7 +86,7 @@
             LOG_BUFFER_LARGE_TILES_SPECS_CHANGE_TAG,
             LogLevel.DEBUG,
             { str1 = specs.toString() },
-            { "Large tiles change: $str1" }
+            { "Large tiles change: $str1" },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
index 1f8a24a1..35faa97 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
@@ -17,9 +17,10 @@
 package com.android.systemui.qs.panels.ui.compose
 
 import android.content.ClipData
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.draganddrop.dragAndDropSource
 import androidx.compose.foundation.draganddrop.dragAndDropTarget
-import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
 import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
 import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.runtime.Composable
@@ -104,11 +105,10 @@
 @Composable
 fun Modifier.dragAndDropTileList(
     gridState: LazyGridState,
-    contentOffset: Offset,
+    contentOffset: () -> Offset,
     dragAndDropState: DragAndDropState,
-    onDrop: () -> Unit,
+    onDrop: (TileSpec) -> Unit,
 ): Modifier {
-    val currentContentOffset by rememberUpdatedState(contentOffset)
     val target =
         remember(dragAndDropState) {
             object : DragAndDropTarget {
@@ -118,7 +118,7 @@
 
                 override fun onMoved(event: DragAndDropEvent) {
                     // Drag offset relative to the list's top left corner
-                    val relativeDragOffset = event.dragOffsetRelativeTo(currentContentOffset)
+                    val relativeDragOffset = event.dragOffsetRelativeTo(contentOffset())
                     val targetItem =
                         gridState.layoutInfo.visibleItemsInfo.firstOrNull { item ->
                             // Check if the drag is on this item
@@ -132,7 +132,7 @@
 
                 override fun onDrop(event: DragAndDropEvent): Boolean {
                     return dragAndDropState.draggedCell?.let {
-                        onDrop()
+                        onDrop(it.tile.tileSpec)
                         dragAndDropState.onDrop()
                         true
                     } ?: false
@@ -158,36 +158,39 @@
     return item.span != 1 && offset.x > itemCenter.x
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun Modifier.dragAndDropTileSource(
     sizedTile: SizedTile<EditTileViewModel>,
     dragAndDropState: DragAndDropState,
-    onTap: (TileSpec) -> Unit,
-    onDoubleTap: (TileSpec) -> Unit = {},
+    onDragStart: () -> Unit,
 ): Modifier {
-    val state by rememberUpdatedState(dragAndDropState)
-    return dragAndDropSource {
-        detectTapGestures(
-            onTap = { onTap(sizedTile.tile.tileSpec) },
-            onDoubleTap = { onDoubleTap(sizedTile.tile.tileSpec) },
-            onLongPress = {
-                state.onStarted(sizedTile)
+    val dragState by rememberUpdatedState(dragAndDropState)
+    @Suppress("DEPRECATION") // b/368361871
+    return dragAndDropSource(
+        block = {
+            detectDragGesturesAfterLongPress(
+                onDrag = { _, _ -> },
+                onDragStart = {
+                    dragState.onStarted(sizedTile)
+                    onDragStart()
 
-                // The tilespec from the ClipData transferred isn't actually needed as we're moving
-                // a tile within the same application. We're using a custom MIME type to limit the
-                // drag event to QS.
-                startTransfer(
-                    DragAndDropTransferData(
-                        ClipData(
-                            QsDragAndDrop.CLIPDATA_LABEL,
-                            arrayOf(QsDragAndDrop.TILESPEC_MIME_TYPE),
-                            ClipData.Item(sizedTile.tile.tileSpec.spec),
+                    // The tilespec from the ClipData transferred isn't actually needed as we're
+                    // moving a tile within the same application. We're using a custom MIME type to
+                    // limit the drag event to QS.
+                    startTransfer(
+                        DragAndDropTransferData(
+                            ClipData(
+                                QsDragAndDrop.CLIPDATA_LABEL,
+                                arrayOf(QsDragAndDrop.TILESPEC_MIME_TYPE),
+                                ClipData.Item(sizedTile.tile.tileSpec.spec),
+                            )
                         )
                     )
-                )
-            },
-        )
-    }
+                },
+            )
+        }
+    )
 }
 
 private object QsDragAndDrop {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 4830ba7..770fd78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -42,10 +42,8 @@
 }
 
 /** Holds the temporary state of the tile list during a drag movement where we move tiles around. */
-class EditTileListState(
-    tiles: List<SizedTile<EditTileViewModel>>,
-    private val columns: Int,
-) : DragAndDropState {
+class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val columns: Int) :
+    DragAndDropState {
     private val _draggedCell = mutableStateOf<SizedTile<EditTileViewModel>?>(null)
     override val draggedCell
         get() = _draggedCell.value
@@ -62,10 +60,37 @@
         return _tiles.filterIsInstance<TileGridCell>().map { it.tile.tileSpec }
     }
 
-    fun indexOf(tileSpec: TileSpec): Int {
+    private fun indexOf(tileSpec: TileSpec): Int {
         return _tiles.indexOfFirst { it is TileGridCell && it.tile.tileSpec == tileSpec }
     }
 
+    /**
+     * Whether the tile with this [TileSpec] is currently an icon in the [EditTileListState]
+     *
+     * @return true if the tile is an icon, false if it's large, null if the tile isn't in the list
+     */
+    fun isIcon(tileSpec: TileSpec): Boolean? {
+        val index = indexOf(tileSpec)
+        return if (index != -1) {
+            val cell = _tiles[index]
+            cell as TileGridCell
+            return cell.isIcon
+        } else {
+            null
+        }
+    }
+
+    /** Toggle the size of the tile corresponding to the [TileSpec] */
+    fun toggleSize(tileSpec: TileSpec) {
+        val fromIndex = indexOf(tileSpec)
+        if (fromIndex != -1) {
+            val cell = _tiles.removeAt(fromIndex)
+            cell as TileGridCell
+            _tiles.add(fromIndex, cell.copy(width = if (cell.isIcon) 2 else 1))
+            regenerateGrid(fromIndex)
+        }
+    }
+
     override fun isMoving(tileSpec: TileSpec): Boolean {
         return _draggedCell.value?.let { it.tile.tileSpec == tileSpec } ?: false
     }
@@ -73,8 +98,8 @@
     override fun onStarted(cell: SizedTile<EditTileViewModel>) {
         _draggedCell.value = cell
 
-        // Add visible spacers to the grid to indicate where the user can move a tile
-        regenerateGrid(includeSpacers = true)
+        // Add spacers to the grid to indicate where the user can move a tile
+        regenerateGrid()
     }
 
     override fun onMoved(target: Int, insertAfter: Boolean) {
@@ -88,14 +113,15 @@
         val insertionIndex = if (insertAfter) target + 1 else target
         if (fromIndex != -1) {
             val cell = _tiles.removeAt(fromIndex)
-            regenerateGrid(includeSpacers = true)
+            regenerateGrid()
             _tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell)
         } else {
-            // Add the tile with a temporary row which will get reassigned when regenerating spacers
+            // Add the tile with a temporary row which will get reassigned when
+            // regenerating spacers
             _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0))
         }
 
-        regenerateGrid(includeSpacers = true)
+        regenerateGrid()
     }
 
     override fun movedOutOfBounds() {
@@ -110,13 +136,28 @@
         _draggedCell.value = null
 
         // Remove the spacers
-        regenerateGrid(includeSpacers = false)
+        regenerateGrid()
     }
 
-    private fun regenerateGrid(includeSpacers: Boolean) {
-        _tiles.filterIsInstance<TileGridCell>().toGridCells(columns, includeSpacers).let {
+    /** Regenerate the list of [GridCell] with their new potential rows */
+    private fun regenerateGrid() {
+        _tiles.filterIsInstance<TileGridCell>().toGridCells(columns).let {
             _tiles.clear()
             _tiles.addAll(it)
         }
     }
+
+    /**
+     * Regenerate the list of [GridCell] with their new potential rows from [fromIndex], leaving
+     * cells before that untouched.
+     */
+    private fun regenerateGrid(fromIndex: Int) {
+        val fromRow = _tiles[fromIndex].row
+        val (pre, post) = _tiles.partition { it.row < fromRow }
+        post.filterIsInstance<TileGridCell>().toGridCells(columns, startingRow = fromRow).let {
+            _tiles.clear()
+            _tiles.addAll(pre)
+            _tiles.addAll(it)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
index fd276c2..0c02b40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.TileRow
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
@@ -27,7 +28,7 @@
 /** A layout of tiles, indicating how they should be composed when showing in QS or in edit mode. */
 interface GridLayout {
     @Composable
-    fun TileGrid(
+    fun SceneScope.TileGrid(
         tiles: List<TileViewModel>,
         modifier: Modifier,
         editModeStart: () -> Unit,
@@ -66,7 +67,7 @@
          */
         fun splitInRows(
             tiles: List<SizedTile<TileViewModel>>,
-            columns: Int
+            columns: Int,
         ): List<List<SizedTile<TileViewModel>>> {
             val row = TileRow<TileViewModel>(columns)
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
index 7de22161..331aabb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
@@ -31,9 +31,11 @@
 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.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.semantics.pageLeft
 import androidx.compose.ui.semantics.pageRight
 import androidx.compose.ui.semantics.semantics
@@ -60,68 +62,64 @@
     val inPageTransition by
         remember(pagerState) {
             derivedStateOf {
-                pagerState.currentPageOffsetFraction.absoluteValue > 0.01 &&
+                pagerState.currentPageOffsetFraction.absoluteValue > 0.05 &&
                     !pagerState.isOverscrolling()
             }
         }
     val coroutineScope = rememberCoroutineScope()
-    Row(
-        modifier =
-            modifier
-                .wrapContentWidth()
-                .pagerDotsSemantics(
-                    pagerState,
-                    coroutineScope,
+    val doubleDotWidth = dotSize * 2 + spaceSize
+    val activeMarkerWidth by
+        animateDpAsState(
+            targetValue = if (inPageTransition) doubleDotWidth else dotSize,
+            label = "PagerDotsTransitionAnimation",
+        )
+    val cornerRadius = dotSize / 2
+
+    fun DrawScope.drawDoubleRect(withPrevious: Boolean, width: Dp) {
+        drawRoundRect(
+            topLeft =
+                Offset(
+                    if (withPrevious) {
+                        dotSize.toPx() - width.toPx()
+                    } else {
+                        -(dotSize.toPx() + spaceSize.toPx())
+                    },
+                    0f,
                 ),
+            color = activeColor,
+            size = Size(width.toPx(), dotSize.toPx()),
+            cornerRadius = CornerRadius(cornerRadius.toPx()),
+        )
+    }
+
+    Row(
+        modifier = modifier.wrapContentWidth().pagerDotsSemantics(pagerState, coroutineScope),
         horizontalArrangement = spacedBy(spaceSize),
-        verticalAlignment = Alignment.CenterVertically
+        verticalAlignment = Alignment.CenterVertically,
     ) {
-        if (!inPageTransition) {
-            repeat(pagerState.pageCount) { i ->
-                // We use canvas directly to only invalidate the draw phase when the page is
-                // changing.
-                Canvas(Modifier.size(dotSize)) {
-                    if (pagerState.currentPage == i) {
-                        drawCircle(activeColor)
-                    } else {
-                        drawCircle(nonActiveColor)
-                    }
-                }
-            }
-        } else {
-            val doubleDotWidth = dotSize * 2 + spaceSize
-            val cornerRadius = dotSize / 2
-            val width by
-                animateDpAsState(targetValue = if (inPageTransition) doubleDotWidth else dotSize)
-
-            fun DrawScope.drawDoubleRect() {
-                drawRoundRect(
-                    color = activeColor,
-                    size = Size(width.toPx(), dotSize.toPx()),
-                    cornerRadius = CornerRadius(cornerRadius.toPx(), cornerRadius.toPx())
-                )
-            }
-
-            repeat(pagerState.pageCount) { page ->
-                Canvas(Modifier.size(dotSize)) {
-                    val withPrevious = pagerState.currentPageOffsetFraction < 0
-                    val ltr = layoutDirection == LayoutDirection.Ltr
+        // This means that the active rounded rect has to be drawn between the current page
+        // and the previous one (as we are animating back), or the current one if not transitioning
+        val withPrevious = pagerState.currentPageOffsetFraction <= 0 || pagerState.isOverscrolling()
+        repeat(pagerState.pageCount) { page ->
+            Canvas(Modifier.size(dotSize)) {
+                val rtl = layoutDirection == LayoutDirection.Rtl
+                scale(if (rtl) -1f else 1f, 1f, Offset(0f, center.y)) {
+                    drawCircle(nonActiveColor)
+                    // We always want to draw the rounded rect on the rightmost dot iteration, so
+                    // the inactive dot is always drawn behind.
+                    // This means that:
+                    // * if we are scrolling back, we draw it when we are in the current page (so it
+                    //   extends between this page and the previous one).
+                    // * if we are scrolling forward, we draw it when we are in the next page (so it
+                    //   extends between the next page and the current one).
+                    // * if we are not scrolling, withPrevious is true (pageOffset 0) and we
+                    //   draw in the current page.
+                    // drawDoubleRect calculates the offset based on the above.
                     if (
-                        withPrevious && page == (pagerState.currentPage - 1) ||
-                            !withPrevious && page == pagerState.currentPage
-                    ) {
-                        if (ltr) {
-                            drawDoubleRect()
-                        }
-                    } else if (
                         withPrevious && page == pagerState.currentPage ||
-                            !withPrevious && page == (pagerState.currentPage + 1)
+                            (!withPrevious && page == pagerState.currentPage + 1)
                     ) {
-                        if (!ltr) {
-                            drawDoubleRect()
-                        }
-                    } else {
-                        drawCircle(nonActiveColor)
+                        drawDoubleRect(withPrevious, activeMarkerWidth)
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 08a56bf..083f529 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -39,6 +39,7 @@
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
 import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
@@ -55,7 +56,7 @@
     @PaginatedBaseLayoutType private val delegateGridLayout: PaginatableGridLayout,
 ) : GridLayout by delegateGridLayout {
     @Composable
-    override fun TileGrid(
+    override fun SceneScope.TileGrid(
         tiles: List<TileViewModel>,
         modifier: Modifier,
         editModeStart: () -> Unit,
@@ -85,16 +86,16 @@
             ) {
                 val page = pages[it]
 
-                delegateGridLayout.TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
+                with(delegateGridLayout) {
+                    TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
+                }
             }
-            Box(
-                modifier = Modifier.height(FooterHeight).fillMaxWidth(),
-            ) {
+            Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) {
                 PagerDots(
                     pagerState = pagerState,
                     activeColor = MaterialTheme.colorScheme.primary,
                     nonActiveColor = MaterialTheme.colorScheme.surfaceVariant,
-                    modifier = Modifier.align(Alignment.Center)
+                    modifier = Modifier.align(Alignment.Center),
                 )
                 CompositionLocalProvider(value = LocalContentColor provides Color.White) {
                     IconButton(
@@ -103,7 +104,7 @@
                     ) {
                         Icon(
                             imageVector = Icons.Default.Edit,
-                            contentDescription = stringResource(id = R.string.qs_edit)
+                            contentDescription = stringResource(id = R.string.qs_edit),
                         )
                     }
                 }
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 f4acbec..8998a7f 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
@@ -16,21 +16,28 @@
 
 package com.android.systemui.qs.panels.ui.compose
 
-import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.util.fastMap
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
+import com.android.systemui.qs.composefragment.ui.GridAnchor
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile
-import com.android.systemui.qs.panels.ui.compose.infinitegrid.TileLazyGrid
 import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
+import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
+import com.android.systemui.res.R
 
 @Composable
-fun QuickQuickSettings(viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier) {
+fun SceneScope.QuickQuickSettings(
+    viewModel: QuickQuickSettingsViewModel,
+    modifier: Modifier = Modifier,
+) {
     val sizedTiles by
         viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList())
     val tiles = sizedTiles.fastMap { it.tile }
@@ -41,20 +48,20 @@
         onDispose { tiles.forEach { it.stopListening(token) } }
     }
     val columns by viewModel.columns.collectAsStateWithLifecycle()
-
-    TileLazyGrid(
-        modifier = modifier.sysuiResTag("qqs_tile_layout"),
-        columns = GridCells.Fixed(columns),
-    ) {
-        items(
-            sizedTiles.size,
-            key = { index -> sizedTiles[index].tile.spec.spec },
-            span = { index -> GridItemSpan(sizedTiles[index].width) },
-        ) { index ->
+    Box(modifier = modifier) {
+        GridAnchor()
+        VerticalSpannedGrid(
+            columns = columns,
+            columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
+            rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
+            spans = sizedTiles.fastMap { it.width },
+            modifier = Modifier.sysuiResTag("qqs_tile_layout"),
+        ) { spanIndex ->
+            val it = sizedTiles[spanIndex]
             Tile(
-                tile = sizedTiles[index].tile,
-                iconOnly = sizedTiles[index].isIcon,
-                modifier = Modifier,
+                tile = it.tile,
+                iconOnly = it.isIcon,
+                modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
index 8c57d41..1a5297b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
@@ -20,16 +20,17 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
 
 @Composable
-fun TileGrid(
+fun SceneScope.TileGrid(
     viewModel: TileGridViewModel,
     modifier: Modifier = Modifier,
-    editModeStart: () -> Unit
+    editModeStart: () -> Unit,
 ) {
     val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
     val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList())
 
-    gridLayout.TileGrid(tiles, modifier, editModeStart)
+    with(gridLayout) { TileGrid(tiles, modifier, editModeStart) }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index aeb6031..8c2fb25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -127,13 +127,14 @@
 }
 
 @Composable
-private fun LargeTileLabels(
+fun LargeTileLabels(
     label: String,
     secondaryLabel: String?,
     colors: TileColors,
+    modifier: Modifier = Modifier,
     accessibilityUiState: AccessibilityUiState? = null,
 ) {
-    Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
+    Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
         Text(label, color = colors.label, modifier = Modifier.tileMarquee())
         if (!TextUtils.isEmpty(secondaryLabel)) {
             Text(
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 a43b880..30bafae 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
@@ -20,6 +20,9 @@
 
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.animateIntAsState
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.foundation.ExperimentalFoundationApi
@@ -31,6 +34,7 @@
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxHeight
@@ -38,6 +42,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.lazy.grid.GridCells
@@ -56,23 +61,34 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
+import androidx.compose.ui.BiasAlignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.customActions
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.stateDescription
@@ -82,7 +98,9 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastMap
+import androidx.compose.ui.zIndex
 import com.android.compose.modifiers.background
+import com.android.compose.modifiers.height
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
@@ -92,31 +110,45 @@
 import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList
 import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
+import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
+import com.android.systemui.qs.panels.ui.compose.selection.ResizingHandle
+import com.android.systemui.qs.panels.ui.compose.selection.TileWidths
+import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile
+import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState
+import com.android.systemui.qs.panels.ui.compose.selection.selectableTile
 import com.android.systemui.qs.panels.ui.model.GridCell
 import com.android.systemui.qs.panels.ui.model.SpacerGridCell
 import com.android.systemui.qs.panels.ui.model.TileGridCell
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
-import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.shared.model.groupAndSort
 import com.android.systemui.res.R
+import kotlinx.coroutines.delay
 
 object TileType
 
 @Composable
 fun DefaultEditTileGrid(
-    currentListState: EditTileListState,
+    listState: EditTileListState,
     otherTiles: List<SizedTile<EditTileViewModel>>,
     columns: Int,
     modifier: Modifier,
-    onAddTile: (TileSpec, Int) -> Unit,
     onRemoveTile: (TileSpec) -> Unit,
     onSetTiles: (List<TileSpec>) -> Unit,
-    onResize: (TileSpec) -> Unit,
+    onResize: (TileSpec, toIcon: Boolean) -> Unit,
 ) {
-    val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
-        onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
-    }
+    val currentListState by rememberUpdatedState(listState)
+    val selectionState =
+        rememberSelectionState(
+            onResize = { currentListState.toggleSize(it) },
+            onResizeEnd = { spec ->
+                // Commit the size currently in the list
+                currentListState.isIcon(spec)?.let { onResize(spec, it) }
+            },
+        )
 
     CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
         Column(
@@ -125,11 +157,11 @@
             modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState()),
         ) {
             AnimatedContent(
-                targetState = currentListState.dragInProgress,
+                targetState = listState.dragInProgress,
                 modifier = Modifier.wrapContentSize(),
                 label = "",
             ) { dragIsInProgress ->
-                EditGridHeader(Modifier.dragAndDropRemoveZone(currentListState, onRemoveTile)) {
+                EditGridHeader(Modifier.dragAndDropRemoveZone(listState, onRemoveTile)) {
                     if (dragIsInProgress) {
                         RemoveTileTarget()
                     } else {
@@ -138,11 +170,11 @@
                 }
             }
 
-            CurrentTilesGrid(currentListState, columns, onRemoveTile, onResize, onSetTiles)
+            CurrentTilesGrid(listState, selectionState, columns, onResize, onSetTiles)
 
             // Hide available tiles when dragging
             AnimatedVisibility(
-                visible = !currentListState.dragInProgress,
+                visible = !listState.dragInProgress,
                 enter = fadeIn(),
                 exit = fadeOut(),
             ) {
@@ -153,7 +185,7 @@
                 ) {
                     EditGridHeader { Text(text = "Hold and drag to add tiles.") }
 
-                    AvailableTileGrid(otherTiles, columns, addTileToEnd, currentListState)
+                    AvailableTileGrid(otherTiles, selectionState, columns, listState)
                 }
             }
 
@@ -162,7 +194,7 @@
                 modifier =
                     Modifier.fillMaxWidth()
                         .weight(1f)
-                        .dragAndDropRemoveZone(currentListState, onRemoveTile)
+                        .dragAndDropRemoveZone(listState, onRemoveTile)
             )
         }
     }
@@ -201,52 +233,48 @@
 }
 
 @Composable
-private fun CurrentTilesContainer(content: @Composable () -> Unit) {
-    Box(
-        Modifier.fillMaxWidth()
-            .border(
-                width = 1.dp,
-                color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
-                shape = RoundedCornerShape(48.dp),
-            )
-            .padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
-    ) {
-        content()
-    }
-}
-
-@Composable
 private fun CurrentTilesGrid(
     listState: EditTileListState,
+    selectionState: MutableSelectionState,
     columns: Int,
-    onClick: (TileSpec) -> Unit,
-    onResize: (TileSpec) -> Unit,
+    onResize: (TileSpec, toIcon: Boolean) -> Unit,
     onSetTiles: (List<TileSpec>) -> Unit,
 ) {
     val currentListState by rememberUpdatedState(listState)
-    val tilePadding = CommonTileDefaults.TileArrangementPadding
+    val tileHeight = CommonTileDefaults.TileHeight
+    val totalRows = listState.tiles.lastOrNull()?.row ?: 0
+    val totalHeight by
+        animateDpAsState(
+            gridHeight(totalRows + 1, tileHeight, TileArrangementPadding, CurrentTilesGridPadding),
+            label = "QSEditCurrentTilesGridHeight",
+        )
+    val gridState = rememberLazyGridState()
+    var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) }
 
-    CurrentTilesContainer {
-        val tileHeight = CommonTileDefaults.TileHeight
-        val totalRows = listState.tiles.lastOrNull()?.row ?: 0
-        val totalHeight = gridHeight(totalRows + 1, tileHeight, tilePadding)
-        val gridState = rememberLazyGridState()
-        var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) }
-
-        TileLazyGrid(
-            state = gridState,
-            modifier =
-                Modifier.height(totalHeight)
-                    .dragAndDropTileList(gridState, gridContentOffset, listState) {
-                        onSetTiles(currentListState.tileSpecs())
-                    }
-                    .onGloballyPositioned { coordinates ->
-                        gridContentOffset = coordinates.positionInRoot()
-                    }
-                    .testTag(CURRENT_TILES_GRID_TEST_TAG),
-            columns = GridCells.Fixed(columns),
-        ) {
-            EditTiles(listState.tiles, onClick, listState, onResize = onResize)
+    TileLazyGrid(
+        state = gridState,
+        columns = GridCells.Fixed(columns),
+        contentPadding = PaddingValues(CurrentTilesGridPadding),
+        modifier =
+            Modifier.fillMaxWidth()
+                .height { totalHeight.roundToPx() }
+                .border(
+                    width = 1.dp,
+                    color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
+                    shape = RoundedCornerShape(48.dp),
+                )
+                .dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec ->
+                    onSetTiles(currentListState.tileSpecs())
+                    selectionState.select(spec, manual = false)
+                }
+                .onGloballyPositioned { coordinates ->
+                    gridContentOffset = coordinates.positionInRoot()
+                }
+                .testTag(CURRENT_TILES_GRID_TEST_TAG),
+    ) {
+        EditTiles(listState.tiles, listState, selectionState) { spec ->
+            // Toggle the current size of the tile
+            currentListState.isIcon(spec)?.let { onResize(spec, !it) }
         }
     }
 }
@@ -254,8 +282,8 @@
 @Composable
 private fun AvailableTileGrid(
     tiles: List<SizedTile<EditTileViewModel>>,
+    selectionState: MutableSelectionState,
     columns: Int,
-    onClick: (TileSpec) -> Unit,
     dragAndDropState: DragAndDropState,
 ) {
     // Available tiles aren't visible during drag and drop, so the row isn't needed
@@ -292,7 +320,7 @@
                             cell = tileGridCell,
                             index = index,
                             dragAndDropState = dragAndDropState,
-                            onClick = onClick,
+                            selectionState = selectionState,
                             modifier = Modifier.weight(1f).fillMaxHeight(),
                         )
                     }
@@ -305,8 +333,8 @@
     }
 }
 
-fun gridHeight(rows: Int, tileHeight: Dp, padding: Dp): Dp {
-    return ((tileHeight + padding) * rows) - padding
+fun gridHeight(rows: Int, tileHeight: Dp, tilePadding: Dp, gridPadding: Dp): Dp {
+    return ((tileHeight + tilePadding) * rows) - tilePadding + gridPadding * 2
 }
 
 private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
@@ -318,11 +346,19 @@
     }
 }
 
+/**
+ * Adds a list of [GridCell] to the lazy grid
+ *
+ * @param cells the list of [GridCell]
+ * @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
+ */
 fun LazyGridScope.EditTiles(
     cells: List<GridCell>,
-    onClick: (TileSpec) -> Unit,
     dragAndDropState: DragAndDropState,
-    onResize: (TileSpec) -> Unit = {},
+    selectionState: MutableSelectionState,
+    onToggleSize: (spec: TileSpec) -> Unit,
 ) {
     items(
         count = cells.size,
@@ -347,8 +383,8 @@
                         cell = cell,
                         index = index,
                         dragAndDropState = dragAndDropState,
-                        onClick = onClick,
-                        onResize = onResize,
+                        selectionState = selectionState,
+                        onToggleSize = onToggleSize,
                     )
                 }
             is SpacerGridCell -> SpacerGridCell()
@@ -361,28 +397,177 @@
     cell: TileGridCell,
     index: Int,
     dragAndDropState: DragAndDropState,
-    onClick: (TileSpec) -> Unit,
-    onResize: (TileSpec) -> Unit = {},
+    selectionState: MutableSelectionState,
+    onToggleSize: (spec: TileSpec) -> Unit,
 ) {
-    val onClickActionName = stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
     val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
+    var selected by remember { mutableStateOf(false) }
+    val selectionAlpha by
+        animateFloatAsState(
+            targetValue = if (selected) 1f else 0f,
+            label = "QSEditTileSelectionAlpha",
+        )
 
-    EditTile(
-        tileViewModel = cell.tile,
-        iconOnly = cell.isIcon,
-        modifier =
-            Modifier.animateItem()
-                .semantics(mergeDescendants = true) {
-                    onClick(onClickActionName) { false }
-                    this.stateDescription = stateDescription
-                }
-                .dragAndDropTileSource(
-                    SizedTileImpl(cell.tile, cell.width),
-                    dragAndDropState,
-                    onClick,
-                    onResize,
-                ),
-    )
+    LaunchedEffect(selectionState.selection?.tileSpec) {
+        selectionState.selection?.let {
+            // A delay is introduced on automatic selections such as dragged tiles or reflow caused
+            // by resizing. This avoids clipping issues on the border and resizing handle, as well
+            // as letting the selection animation play correctly.
+            if (!it.manual) {
+                delay(250)
+            }
+        }
+        selected = selectionState.selection?.tileSpec == cell.tile.tileSpec
+    }
+
+    val modifier =
+        Modifier.animateItem()
+            .semantics(mergeDescendants = true) {
+                this.stateDescription = stateDescription
+                contentDescription = cell.tile.label.text
+                customActions =
+                    listOf(
+                        // TODO(b/367748260): Add final accessibility actions
+                        CustomAccessibilityAction("Toggle size") {
+                            onToggleSize(cell.tile.tileSpec)
+                            true
+                        }
+                    )
+            }
+            .height(CommonTileDefaults.TileHeight)
+            .fillMaxWidth()
+
+    val content =
+        @Composable {
+            EditTile(
+                tileViewModel = cell.tile,
+                iconOnly = cell.isIcon,
+                selectionAlpha = { selectionAlpha },
+                modifier =
+                    Modifier.fillMaxSize()
+                        .selectableTile(cell.tile.tileSpec, selectionState)
+                        .dragAndDropTileSource(
+                            SizedTileImpl(cell.tile, cell.width),
+                            dragAndDropState,
+                            selectionState::unSelect,
+                        ),
+            )
+        }
+
+    if (selected) {
+        SelectedTile(
+            isIcon = cell.isIcon,
+            selectionAlpha = { selectionAlpha },
+            selectionState = selectionState,
+            modifier = modifier.zIndex(2f), // 2f to display this tile over neighbors when dragged
+            content = content,
+        )
+    } else {
+        UnselectedTile(
+            selectionAlpha = { selectionAlpha },
+            selectionState = selectionState,
+            modifier = modifier,
+            content = content,
+        )
+    }
+}
+
+@Composable
+private fun SelectedTile(
+    isIcon: Boolean,
+    selectionAlpha: () -> Float,
+    selectionState: MutableSelectionState,
+    modifier: Modifier = Modifier,
+    content: @Composable () -> Unit,
+) {
+    // Current base, min and max width of this tile
+    var tileWidths: TileWidths? by remember { mutableStateOf(null) }
+
+    // Animated diff between the current width and the resized width of the tile. We can't use
+    // animateContentSize here as the tile is sometimes unbounded.
+    val remainingOffset by
+        animateIntAsState(
+            selectionState.resizingState?.let { tileWidths?.base?.minus(it.width) ?: 0 } ?: 0,
+            label = "QSEditTileWidthOffset",
+        )
+
+    val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() }
+    Box(
+        modifier.onSizeChanged {
+            val min = if (isIcon) it.width else (it.width - padding) / 2
+            val max = if (isIcon) (it.width * 2) + padding else it.width
+            tileWidths = TileWidths(it.width, min, max)
+        }
+    ) {
+        val handle =
+            @Composable {
+                ResizingHandle(
+                    enabled = true,
+                    selectionState = selectionState,
+                    transition = selectionAlpha,
+                    tileWidths = { tileWidths },
+                )
+            }
+
+        Layout(contents = listOf(content, handle)) {
+            (contentMeasurables, handleMeasurables),
+            constraints ->
+            // Grab the width from the resizing state if a resize is in progress, otherwise fill the
+            // max width
+            val width =
+                selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset)
+            val contentPlaceable =
+                contentMeasurables.first().measure(constraints.copy(maxWidth = width))
+            val handlePlaceable = handleMeasurables.first().measure(constraints)
+
+            // Place the dot vertically centered on the right edge
+            val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
+            val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
+
+            layout(constraints.maxWidth, constraints.maxHeight) {
+                contentPlaceable.place(0, 0)
+                handlePlaceable.place(handleX, handleY)
+            }
+        }
+    }
+}
+
+@Composable
+private fun UnselectedTile(
+    selectionAlpha: () -> Float,
+    selectionState: MutableSelectionState,
+    modifier: Modifier = Modifier,
+    content: @Composable () -> Unit,
+) {
+    val handle =
+        @Composable {
+            ResizingHandle(
+                enabled = false,
+                selectionState = selectionState,
+                transition = selectionAlpha,
+            )
+        }
+
+    Box(modifier) {
+        Layout(contents = listOf(content, handle)) {
+            (contentMeasurables, handleMeasurables),
+            constraints ->
+            val contentPlaceable =
+                contentMeasurables
+                    .first()
+                    .measure(constraints.copy(maxWidth = constraints.maxWidth))
+            val handlePlaceable = handleMeasurables.first().measure(constraints)
+
+            // Place the dot vertically centered on the right edge
+            val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
+            val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
+
+            layout(constraints.maxWidth, constraints.maxHeight) {
+                contentPlaceable.place(0, 0)
+                handlePlaceable.place(handleX, handleY)
+            }
+        }
+    }
 }
 
 @Composable
@@ -390,8 +575,8 @@
     cell: TileGridCell,
     index: Int,
     dragAndDropState: DragAndDropState,
+    selectionState: MutableSelectionState,
     modifier: Modifier = Modifier,
-    onClick: (TileSpec) -> Unit,
 ) {
     val onClickActionName = stringResource(id = R.string.accessibility_qs_edit_tile_add_action)
     val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
@@ -403,21 +588,27 @@
         verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top),
         modifier = modifier,
     ) {
-        EditTile(
-            tileViewModel = cell.tile,
-            iconOnly = true,
+        EditTileContainer(
             colors = colors,
             modifier =
-                Modifier.semantics(mergeDescendants = true) {
+                Modifier.fillMaxWidth()
+                    .height(CommonTileDefaults.TileHeight)
+                    .clearSelectionTile(selectionState)
+                    .semantics(mergeDescendants = true) {
                         onClick(onClickActionName) { false }
                         this.stateDescription = stateDescription
                     }
-                    .dragAndDropTileSource(
-                        SizedTileImpl(cell.tile, cell.width),
-                        dragAndDropState,
-                        onTap = onClick,
-                    ),
-        )
+                    .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
+                        selectionState.unSelect()
+                    },
+        ) {
+            // Icon
+            SmallTileContent(
+                icon = cell.tile.icon,
+                color = colors.icon,
+                modifier = Modifier.align(Alignment.Center),
+            )
+        }
         Box(Modifier.fillMaxSize()) {
             Text(
                 cell.tile.label.text,
@@ -434,7 +625,7 @@
 @Composable
 private fun SpacerGridCell(modifier: Modifier = Modifier) {
     // By default, spacers are invisible and exist purely to catch drag movements
-    Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth().tilePadding())
+    Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth())
 }
 
 @Composable
@@ -443,20 +634,35 @@
     iconOnly: Boolean,
     modifier: Modifier = Modifier,
     colors: TileColors = EditModeTileDefaults.editTileColors(),
+    selectionAlpha: () -> Float = { 1f },
 ) {
-    EditTileContainer(colors = colors, modifier = modifier) {
-        if (iconOnly) {
+    // Animated horizontal alignment from center (0f) to start (-1f)
+    val alignmentValue by
+        animateFloatAsState(
+            targetValue = if (iconOnly) 0f else -1f,
+            label = "QSEditTileContentAlignment",
+        )
+    val alignment by remember {
+        derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) }
+    }
+
+    EditTileContainer(colors = colors, selectionAlpha = selectionAlpha, modifier = modifier) {
+        // Icon
+        Box(Modifier.size(ToggleTargetSize).align(alignment)) {
             SmallTileContent(
                 icon = tileViewModel.icon,
                 color = colors.icon,
                 modifier = Modifier.align(Alignment.Center),
             )
-        } else {
-            LargeTileContent(
+        }
+
+        // Labels, positioned after the icon
+        AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
+            LargeTileLabels(
                 label = tileViewModel.label.text,
                 secondaryLabel = tileViewModel.appName?.text,
-                icon = tileViewModel.icon,
                 colors = colors,
+                modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
             )
         }
     }
@@ -466,27 +672,41 @@
 private fun EditTileContainer(
     colors: TileColors,
     modifier: Modifier = Modifier,
-    content: @Composable BoxScope.() -> Unit,
+    selectionAlpha: () -> Float = { 0f },
+    selectionColor: Color = MaterialTheme.colorScheme.primary,
+    content: @Composable BoxScope.() -> Unit = {},
 ) {
     Box(
-        modifier =
-            modifier
-                .height(CommonTileDefaults.TileHeight)
-                .fillMaxWidth()
-                .drawBehind {
-                    drawRoundRect(
-                        SolidColor(colors.background),
-                        cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
-                    )
-                }
-                .tilePadding(),
-        content = content,
-    )
+        Modifier.wrapContentSize().drawWithContent {
+            drawContent()
+            drawRoundRect(
+                SolidColor(selectionColor),
+                cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
+                style = Stroke(EditModeTileDefaults.SelectedBorderWidth.toPx()),
+                alpha = selectionAlpha(),
+            )
+        }
+    ) {
+        Box(
+            modifier =
+                modifier
+                    .drawBehind {
+                        drawRoundRect(
+                            SolidColor(colors.background),
+                            cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
+                        )
+                    }
+                    .tilePadding(),
+            content = content,
+        )
+    }
 }
 
 private object EditModeTileDefaults {
     const val PLACEHOLDER_ALPHA = .3f
     val EditGridHeaderHeight = 60.dp
+    val SelectedBorderWidth = 2.dp
+    val CurrentTilesGridPadding = 8.dp
 
     @Composable
     fun editTileColors(): TileColors =
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 f96c27d..e6edba5 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
@@ -16,15 +16,17 @@
 
 package com.android.systemui.qs.panels.ui.compose.infinitegrid
 
-import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.util.fastMap
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
 import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
 import com.android.systemui.qs.panels.ui.compose.rememberEditListState
@@ -33,6 +35,8 @@
 import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
+import com.android.systemui.res.R
 import javax.inject.Inject
 
 @SysUISingleton
@@ -44,7 +48,7 @@
 ) : PaginatableGridLayout {
 
     @Composable
-    override fun TileGrid(
+    override fun SceneScope.TileGrid(
         tiles: List<TileViewModel>,
         modifier: Modifier,
         editModeStart: () -> Unit,
@@ -57,15 +61,18 @@
         val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
         val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
 
-        TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
-            items(sizedTiles.size, span = { index -> GridItemSpan(sizedTiles[index].width) }) {
-                index ->
-                Tile(
-                    tile = sizedTiles[index].tile,
-                    iconOnly = iconTilesViewModel.isIconTile(sizedTiles[index].tile.spec),
-                    modifier = Modifier,
-                )
-            }
+        VerticalSpannedGrid(
+            columns = columns,
+            columnSpacing = dimensionResource(R.dimen.qs_tile_margin_horizontal),
+            rowSpacing = dimensionResource(R.dimen.qs_tile_margin_vertical),
+            spans = sizedTiles.fastMap { it.width },
+        ) { spanIndex ->
+            val it = sizedTiles[spanIndex]
+            Tile(
+                tile = it.tile,
+                iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
+                modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
+            )
         }
     }
 
@@ -94,11 +101,10 @@
         val (currentTiles, otherTiles) = sizedTiles.partition { it.tile.isCurrent }
         val currentListState = rememberEditListState(currentTiles, columns)
         DefaultEditTileGrid(
-            currentListState = currentListState,
+            listState = currentListState,
             otherTiles = otherTiles,
             columns = columns,
             modifier = modifier,
-            onAddTile = onAddTile,
             onRemoveTile = onRemoveTile,
             onSetTiles = onSetTiles,
             onResize = iconTilesViewModel::resize,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 45aad82..afcbed6d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -29,6 +29,7 @@
 import androidx.compose.foundation.layout.Arrangement.spacedBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
@@ -82,6 +83,7 @@
     columns: GridCells,
     modifier: Modifier = Modifier,
     state: LazyGridState = rememberLazyGridState(),
+    contentPadding: PaddingValues = PaddingValues(0.dp),
     content: LazyGridScope.() -> Unit,
 ) {
     LazyVerticalGrid(
@@ -89,6 +91,7 @@
         columns = columns,
         verticalArrangement = spacedBy(CommonTileDefaults.TileArrangementPadding),
         horizontalArrangement = spacedBy(CommonTileDefaults.TileArrangementPadding),
+        contentPadding = contentPadding,
         modifier = modifier,
         content = content,
     )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
new file mode 100644
index 0000000..441d962
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.qs.panels.ui.compose.selection
+
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+/** Creates the state of the current selected tile that is remembered across compositions. */
+@Composable
+fun rememberSelectionState(
+    onResize: (TileSpec) -> Unit,
+    onResizeEnd: (TileSpec) -> Unit,
+): MutableSelectionState {
+    return remember { MutableSelectionState(onResize, onResizeEnd) }
+}
+
+/**
+ * Holds the selected [TileSpec] and whether the selection was manual, i.e. caused by a tap from the
+ * user.
+ */
+data class Selection(val tileSpec: TileSpec, val manual: Boolean)
+
+/** Holds the state of the current selection. */
+class MutableSelectionState(
+    val onResize: (TileSpec) -> Unit,
+    private val onResizeEnd: (TileSpec) -> Unit,
+) {
+    private var _selection = mutableStateOf<Selection?>(null)
+    private var _resizingState = mutableStateOf<ResizingState?>(null)
+
+    /** The [Selection] if a tile is selected, null if not. */
+    val selection by _selection
+
+    /** The [ResizingState] of the selected tile is currently being resized, null if not. */
+    val resizingState by _resizingState
+
+    fun select(tileSpec: TileSpec, manual: Boolean) {
+        _selection.value = Selection(tileSpec, manual)
+    }
+
+    fun unSelect() {
+        _selection.value = null
+        onResizingDragEnd()
+    }
+
+    fun onResizingDrag(offset: Float) {
+        _resizingState.value?.onDrag(offset)
+    }
+
+    fun onResizingDragStart(tileWidths: TileWidths) {
+        _selection.value?.let {
+            _resizingState.value = ResizingState(tileWidths) { onResize(it.tileSpec) }
+        }
+    }
+
+    fun onResizingDragEnd() {
+        _resizingState.value = null
+        _selection.value?.let {
+            onResizeEnd(it.tileSpec)
+
+            // Mark the selection as automatic in case the tile ends up moving to a different
+            // row with its new size.
+            _selection.value = it.copy(manual = false)
+        }
+    }
+}
+
+/**
+ * Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as
+ * they can be selected.
+ */
+@Composable
+fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier {
+    return pointerInput(Unit) {
+        detectTapGestures(
+            onTap = {
+                if (selectionState.selection?.tileSpec == tileSpec) {
+                    selectionState.unSelect()
+                } else {
+                    selectionState.select(tileSpec, manual = true)
+                }
+            }
+        )
+    }
+}
+
+/**
+ * Listens for click events to unselect any tile. Use this on available tiles as they can't be
+ * selected.
+ */
+@Composable
+fun Modifier.clearSelectionTile(selectionState: MutableSelectionState): Modifier {
+    return pointerInput(Unit) { detectTapGestures(onTap = { selectionState.unSelect() }) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
new file mode 100644
index 0000000..a084bc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose.selection
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.setValue
+import com.android.systemui.qs.panels.ui.compose.selection.ResizingDefaults.RESIZING_THRESHOLD
+
+class ResizingState(private val widths: TileWidths, private val onResize: () -> Unit) {
+    // Total drag offset of this resize operation
+    private var totalOffset = 0f
+
+    /** Width in pixels of the resizing tile. */
+    var width by mutableIntStateOf(widths.base)
+
+    // Whether the tile is currently over the threshold and should be a large tile
+    private var passedThreshold: Boolean = passedThreshold(calculateProgression(width))
+
+    fun onDrag(offset: Float) {
+        totalOffset += offset
+        width = (widths.base + totalOffset).toInt().coerceIn(widths.min, widths.max)
+
+        passedThreshold(calculateProgression(width)).let {
+            // Resize if we went over the threshold
+            if (passedThreshold != it) {
+                passedThreshold = it
+                onResize()
+            }
+        }
+    }
+
+    private fun passedThreshold(progression: Float): Boolean {
+        return progression >= RESIZING_THRESHOLD
+    }
+
+    /** The progression of the resizing tile between an icon tile (0f) and a large tile (1f) */
+    private fun calculateProgression(width: Int): Float {
+        return ((width - widths.min) / (widths.max - widths.min).toFloat()).coerceIn(0f, 1f)
+    }
+}
+
+/** Holds the width of a tile as well as its min and max widths */
+data class TileWidths(val base: Int, val min: Int, val max: Int) {
+    init {
+        check(max > min) { "The max width needs to be larger than the min width." }
+    }
+}
+
+private object ResizingDefaults {
+    const val RESIZING_THRESHOLD = .25f
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
new file mode 100644
index 0000000..e0f0b6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.qs.panels.ui.compose.selection
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.gestures.detectHorizontalDragGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.systemGestureExclusion
+import androidx.compose.material3.LocalMinimumInteractiveComponentSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.toSize
+import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize
+
+/**
+ * Dot handling resizing drag events. Use this on the selected tile to resize it
+ *
+ * @param enabled whether resizing drag events should be handled
+ * @param selectionState the [MutableSelectionState] on the grid
+ * @param transition the animated value for the dot, used for its alpha and scale
+ * @param tileWidths the [TileWidths] of the selected tile
+ * @param onResize the callback when the drag passes the resizing threshold
+ */
+@Composable
+fun ResizingHandle(
+    enabled: Boolean,
+    selectionState: MutableSelectionState,
+    transition: () -> Float,
+    tileWidths: () -> TileWidths? = { null },
+) {
+    if (enabled) {
+        // Manually creating the touch target around the resizing dot to ensure that the next tile
+        // does
+        // not receive the touch input accidentally.
+        val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current
+        Box(
+            Modifier.size(minTouchTargetSize)
+                .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) }
+                .pointerInput(Unit) {
+                    detectHorizontalDragGestures(
+                        onHorizontalDrag = { _, offset -> selectionState.onResizingDrag(offset) },
+                        onDragStart = {
+                            tileWidths()?.let { selectionState.onResizingDragStart(it) }
+                        },
+                        onDragEnd = selectionState::onResizingDragEnd,
+                        onDragCancel = selectionState::onResizingDragEnd,
+                    )
+                }
+        ) {
+            ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
+        }
+    } else {
+        ResizingDot(transition = transition)
+    }
+}
+
+@Composable
+private fun ResizingDot(
+    transition: () -> Float,
+    modifier: Modifier = Modifier,
+    color: Color = MaterialTheme.colorScheme.primary,
+) {
+    Canvas(modifier = modifier.size(ResizingDotSize)) {
+        val v = transition()
+        drawCircle(color = color, radius = (ResizingDotSize / 2).toPx() * v, alpha = v)
+    }
+}
+
+private object SelectionDefaults {
+    val ResizingDotSize = 16.dp
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
index b16a707..b1841c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
@@ -27,6 +27,7 @@
 sealed interface GridCell {
     val row: Int
     val span: GridItemSpan
+    val s: String
 }
 
 /**
@@ -39,6 +40,7 @@
     override val row: Int,
     override val width: Int,
     override val span: GridItemSpan = GridItemSpan(width),
+    override val s: String = "${tile.tileSpec.spec}-$row-$width",
 ) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile {
     val key: String = "${tile.tileSpec.spec}-$row"
 
@@ -53,22 +55,30 @@
 data class SpacerGridCell(
     override val row: Int,
     override val span: GridItemSpan = GridItemSpan(1),
+    override val s: String = "spacer",
 ) : GridCell
 
+/**
+ * Generates a list of [GridCell] from a list of [SizedTile]
+ *
+ * Builds rows based on the tiles' widths, and fill each hole with a [SpacerGridCell]
+ *
+ * @param startingRow The row index the grid is built from, used in cases where only end rows need
+ *   to be regenerated
+ */
 fun List<SizedTile<EditTileViewModel>>.toGridCells(
     columns: Int,
-    includeSpacers: Boolean = false,
+    startingRow: Int = 0,
 ): List<GridCell> {
     return splitInRowsSequence(this, columns)
         .flatMapIndexed { rowIndex, sizedTiles ->
-            val row: List<GridCell> = sizedTiles.map { TileGridCell(it, rowIndex) }
+            val correctedRowIndex = rowIndex + startingRow
+            val row: List<GridCell> = sizedTiles.map { TileGridCell(it, correctedRowIndex) }
 
-            if (includeSpacers) {
-                // Fill the incomplete rows with spacers
-                val numSpacers = columns - sizedTiles.sumOf { it.width }
-                row.toMutableList().apply { repeat(numSpacers) { add(SpacerGridCell(rowIndex)) } }
-            } else {
-                row
+            // Fill the incomplete rows with spacers
+            val numSpacers = columns - sizedTiles.sumOf { it.width }
+            row.toMutableList().apply {
+                repeat(numSpacers) { add(SpacerGridCell(correctedRowIndex)) }
             }
         }
         .toList()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
index b604e18..4e698ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
@@ -27,7 +27,7 @@
 
     fun isIconTile(spec: TileSpec): Boolean
 
-    fun resize(spec: TileSpec)
+    fun resize(spec: TileSpec, toIcon: Boolean)
 }
 
 @SysUISingleton
@@ -37,5 +37,5 @@
 
     override fun isIconTile(spec: TileSpec): Boolean = interactor.isIconTile(spec)
 
-    override fun resize(spec: TileSpec) = interactor.resize(spec)
+    override fun resize(spec: TileSpec, toIcon: Boolean) = interactor.resize(spec, toIcon)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/shared/ui/ElementKeys.kt b/packages/SystemUI/src/com/android/systemui/qs/shared/ui/ElementKeys.kt
new file mode 100644
index 0000000..625459d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/shared/ui/ElementKeys.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.qs.shared.ui
+
+import com.android.compose.animation.scene.ElementKey
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+/** Element keys to be used by the compose implementation of QS for animations. */
+object ElementKeys {
+    val QuickSettingsContent = ElementKey("QuickSettingsContent")
+    val GridAnchor = ElementKey("QuickSettingsGridAnchor")
+    val FooterActions = ElementKey("FooterActions")
+
+    class TileElementKey(spec: TileSpec, val position: Int) : ElementKey(spec.spec, spec.spec)
+
+    fun TileSpec.toElementKey(positionInGrid: Int) = TileElementKey(this, positionInGrid)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 5ea8c21..a4f3c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.qs.tileimpl;
 
+import static com.android.systemui.Flags.qsNewTiles;
 import static com.android.systemui.Flags.removeUpdateListenerInQsIconViewImpl;
 
 import android.animation.Animator;
@@ -66,12 +67,22 @@
 
     private ValueAnimator mColorAnimator = new ValueAnimator();
 
+    private int mColorUnavailable;
+    private int mColorInactive;
+    private int mColorActive;
+
     public QSIconViewImpl(Context context) {
         super(context);
 
         final Resources res = context.getResources();
         mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_icon_size);
 
+        if (qsNewTiles()) { // pre-load icon tint colors
+            mColorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.outline);
+            mColorInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant);
+            mColorActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive);
+        }
+
         mIcon = createIcon();
         addView(mIcon);
         mColorAnimator.setDuration(QS_ANIM_LENGTH);
@@ -195,7 +206,11 @@
     }
 
     protected int getColor(QSTile.State state) {
-        return getIconColorForState(getContext(), state);
+        if (qsNewTiles()) {
+            return getCachedIconColorForState(state);
+        } else {
+            return getIconColorForState(getContext(), state);
+        }
     }
 
     private void animateGrayScale(int fromColor, int toColor, ImageView iv,
@@ -267,6 +282,19 @@
         }
     }
 
+    private int getCachedIconColorForState(QSTile.State state) {
+        if (state.disabledByPolicy || state.state == Tile.STATE_UNAVAILABLE) {
+            return mColorUnavailable;
+        } else if (state.state == Tile.STATE_INACTIVE) {
+            return mColorInactive;
+        } else if (state.state == Tile.STATE_ACTIVE) {
+            return mColorActive;
+        } else {
+            Log.e("QSIconView", "Invalid state " + state);
+            return 0;
+        }
+    }
+
     private static class EndRunnableAnimatorListener extends AnimatorListenerAdapter {
         private Runnable mRunnable;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 6a8cc17..18b1f07 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -126,12 +126,12 @@
     private val overlayColorActive =
         Utils.applyAlpha(
             /* alpha= */ 0.11f,
-            Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
+            Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive),
         )
     private val overlayColorInactive =
         Utils.applyAlpha(
             /* alpha= */ 0.08f,
-            Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
+            Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive),
         )
 
     private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
@@ -188,10 +188,7 @@
     private var lastState = INVALID
     private var lastIconTint = 0
     private val launchableViewDelegate =
-        LaunchableViewDelegate(
-            this,
-            superSetVisibility = { super.setVisibility(it) },
-        )
+        LaunchableViewDelegate(this, superSetVisibility = { super.setVisibility(it) })
     private var lastDisabledByPolicy = false
 
     private val locInScreen = IntArray(2)
@@ -418,7 +415,7 @@
             initLongPressEffectCallback()
             init(
                 { _: View -> longPressEffect.onTileClick() },
-                null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
+                { _: View -> true }, // Haptics and long-clicks are handled by [QSLongPressEffect]
             )
         } else {
             val expandable = Expandable.fromView(this)
@@ -583,7 +580,7 @@
                     AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id,
                     resources.getString(
                         R.string.accessibility_tile_disabled_by_policy_action_description
-                    )
+                    ),
                 )
             )
         } else {
@@ -591,7 +588,7 @@
                 info.addAction(
                     AccessibilityNodeInfo.AccessibilityAction(
                         AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
-                        resources.getString(R.string.accessibility_long_click_tile)
+                        resources.getString(R.string.accessibility_long_click_tile),
                     )
                 )
             }
@@ -646,7 +643,6 @@
     }
 
     // HANDLE STATE CHANGES RELATED METHODS
-
     protected open fun handleStateChanged(state: QSTile.State) {
         val allowAnimations = animationsEnabled()
         isClickable = state.state != Tile.STATE_UNAVAILABLE
@@ -716,35 +712,35 @@
                 state.spec,
                 state.state,
                 state.disabledByPolicy,
-                getBackgroundColorForState(state.state, state.disabledByPolicy)
+                getBackgroundColorForState(state.state, state.disabledByPolicy),
             )
             if (allowAnimations) {
                 singleAnimator.setValues(
                     colorValuesHolder(
                         BACKGROUND_NAME,
                         backgroundColor,
-                        getBackgroundColorForState(state.state, state.disabledByPolicy)
+                        getBackgroundColorForState(state.state, state.disabledByPolicy),
                     ),
                     colorValuesHolder(
                         LABEL_NAME,
                         label.currentTextColor,
-                        getLabelColorForState(state.state, state.disabledByPolicy)
+                        getLabelColorForState(state.state, state.disabledByPolicy),
                     ),
                     colorValuesHolder(
                         SECONDARY_LABEL_NAME,
                         secondaryLabel.currentTextColor,
-                        getSecondaryLabelColorForState(state.state, state.disabledByPolicy)
+                        getSecondaryLabelColorForState(state.state, state.disabledByPolicy),
                     ),
                     colorValuesHolder(
                         CHEVRON_NAME,
                         chevronView.imageTintList?.defaultColor ?: 0,
-                        getChevronColorForState(state.state, state.disabledByPolicy)
+                        getChevronColorForState(state.state, state.disabledByPolicy),
                     ),
                     colorValuesHolder(
                         OVERLAY_NAME,
                         backgroundOverlayColor,
-                        getOverlayColorForState(state.state)
-                    )
+                        getOverlayColorForState(state.state),
+                    ),
                 )
                 singleAnimator.start()
             } else {
@@ -753,7 +749,7 @@
                     getLabelColorForState(state.state, state.disabledByPolicy),
                     getSecondaryLabelColorForState(state.state, state.disabledByPolicy),
                     getChevronColorForState(state.state, state.disabledByPolicy),
-                    getOverlayColorForState(state.state)
+                    getOverlayColorForState(state.state),
                 )
             }
         }
@@ -1077,7 +1073,7 @@
             backgroundColor,
             label.currentTextColor,
             secondaryLabel.currentTextColor,
-            chevronView.imageTintList?.defaultColor ?: 0
+            chevronView.imageTintList?.defaultColor ?: 0,
         )
 
     inner class StateChangeRunnable(private val state: QSTile.State) : Runnable {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
index d0437a7..b8f4ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.base.viewmodel
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import com.android.systemui.dagger.qualifiers.Application
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -27,5 +28,5 @@
 constructor(@Application private val applicationScope: CoroutineScope) {
 
     fun create(): CoroutineScope =
-        CoroutineScope(applicationScope.coroutineContext + SupervisorJob())
+        CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("QSTileScope"))
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 5ea9e6a..301ab2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -311,10 +311,7 @@
         mConfig = MobileMappings.Config.readConfig(mContext);
         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
         mSubIdTelephonyManagerMap.put(mDefaultDataSubId, mTelephonyManager);
-        InternetTelephonyCallback telephonyCallback =
-                new InternetTelephonyCallback(mDefaultDataSubId);
-        mSubIdTelephonyCallbackMap.put(mDefaultDataSubId, telephonyCallback);
-        mTelephonyManager.registerTelephonyCallback(mExecutor, telephonyCallback);
+        registerInternetTelephonyCallback(mTelephonyManager, mDefaultDataSubId);
         // Listen the connectivity changes
         mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback);
         mCanConfigWifi = canConfigWifi;
@@ -346,6 +343,23 @@
         mCallback = null;
     }
 
+    /**
+     * This is to generate and register the new callback to Telephony for uncached subscription id,
+     * then cache it. Telephony also cached this callback into
+     * {@link com.android.server.TelephonyRegistry}, so if subscription id and callback were cached
+     * already, it shall do nothing to avoid registering redundant callback to Telephony.
+     */
+    private void registerInternetTelephonyCallback(
+            TelephonyManager telephonyManager, int subId) {
+        if (mSubIdTelephonyCallbackMap.containsKey(subId)) {
+            // Avoid to generate and register unnecessary callback to Telephony.
+            return;
+        }
+        InternetTelephonyCallback telephonyCallback = new InternetTelephonyCallback(subId);
+        mSubIdTelephonyCallbackMap.put(subId, telephonyCallback);
+        telephonyManager.registerTelephonyCallback(mExecutor, telephonyCallback);
+    }
+
     boolean isAirplaneModeEnabled() {
         return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
     }
@@ -673,9 +687,7 @@
             int subId = subInfo.getSubscriptionId();
             if (mSubIdTelephonyManagerMap.get(subId) == null) {
                 TelephonyManager secondaryTm = mTelephonyManager.createForSubscriptionId(subId);
-                InternetTelephonyCallback telephonyCallback = new InternetTelephonyCallback(subId);
-                secondaryTm.registerTelephonyCallback(mExecutor, telephonyCallback);
-                mSubIdTelephonyCallbackMap.put(subId, telephonyCallback);
+                registerInternetTelephonyCallback(secondaryTm, subId);
                 mSubIdTelephonyManagerMap.put(subId, secondaryTm);
             }
             return subId;
@@ -1351,6 +1363,7 @@
         if (DEBUG) {
             Log.d(TAG, "DDS: defaultDataSubId:" + defaultDataSubId);
         }
+
         if (SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)) {
             // clean up old defaultDataSubId
             TelephonyCallback oldCallback = mSubIdTelephonyCallbackMap.get(mDefaultDataSubId);
@@ -1366,9 +1379,7 @@
             // create for new defaultDataSubId
             mTelephonyManager = mTelephonyManager.createForSubscriptionId(defaultDataSubId);
             mSubIdTelephonyManagerMap.put(defaultDataSubId, mTelephonyManager);
-            InternetTelephonyCallback newCallback = new InternetTelephonyCallback(defaultDataSubId);
-            mSubIdTelephonyCallbackMap.put(defaultDataSubId, newCallback);
-            mTelephonyManager.registerTelephonyCallback(mHandler::post, newCallback);
+            registerInternetTelephonyCallback(mTelephonyManager, defaultDataSubId);
             mCallback.onSubscriptionsChanged(defaultDataSubId);
         }
         mDefaultDataSubId = defaultDataSubId;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index 246fe38..ae56c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.qs.tiles.dialog
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.util.Log
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.DialogCuj
@@ -62,7 +63,7 @@
             }
             return
         } else {
-            coroutineScope = CoroutineScope(bgDispatcher)
+            coroutineScope = CoroutineScope(bgDispatcher + createCoroutineTracingContext("InternetDialogScope"))
             dialog =
                 dialogFactory
                     .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
index 8965ef2..bb0b9b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
@@ -18,7 +18,9 @@
 
 import android.content.Context
 import android.content.res.Resources
+import android.os.Handler
 import android.widget.Switch
+import com.android.settingslib.graph.SignalDrawable
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text.Companion.loadText
@@ -28,6 +30,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
 import javax.inject.Inject
 
 /** Maps [InternetTileModel] to [QSTileState]. */
@@ -37,6 +40,7 @@
     @Main private val resources: Resources,
     private val theme: Resources.Theme,
     private val context: Context,
+    @Main private val handler: Handler,
 ) : QSTileDataToStateMapper<InternetTileModel> {
 
     override fun map(config: QSTileConfig, data: InternetTileModel): QSTileState =
@@ -44,25 +48,42 @@
             label = resources.getString(R.string.quick_settings_internet_label)
             expandedAccessibilityClass = Switch::class
 
-            if (data.secondaryLabel != null) {
-                secondaryLabel = data.secondaryLabel.loadText(context)
-            } else {
-                secondaryLabel = data.secondaryTitle
-            }
+            secondaryLabel =
+                if (data.secondaryLabel != null) {
+                    data.secondaryLabel.loadText(context)
+                } else {
+                    data.secondaryTitle
+                }
 
             stateDescription = data.stateDescription.loadContentDescription(context)
             contentDescription = data.contentDescription.loadContentDescription(context)
 
-            iconRes = data.iconId
-            if (data.icon != null) {
-                this.icon = { data.icon }
-            } else if (data.iconId != null) {
-                val loadedIcon =
-                    Icon.Loaded(
-                        resources.getDrawable(data.iconId!!, theme),
-                        contentDescription = null
-                    )
-                this.icon = { loadedIcon }
+            when (val dataIcon = data.icon) {
+                is InternetTileIconModel.ResourceId -> {
+                    iconRes = dataIcon.resId
+                    icon = {
+                        Icon.Loaded(
+                            resources.getDrawable(dataIcon.resId, theme),
+                            contentDescription = null,
+                        )
+                    }
+                }
+
+                is InternetTileIconModel.Cellular -> {
+                    val signalDrawable = SignalDrawable(context, handler)
+                    signalDrawable.setLevel(dataIcon.level)
+                    icon = { Icon.Loaded(signalDrawable, contentDescription = null) }
+                }
+
+                is InternetTileIconModel.Satellite -> {
+                    iconRes = dataIcon.resourceIcon.res // level is inferred from res
+                    icon = {
+                        Icon.Loaded(
+                            resources.getDrawable(dataIcon.resourceIcon.res, theme),
+                            contentDescription = null,
+                        )
+                    }
+                }
             }
 
             sideViewIcon = QSTileState.SideViewIcon.Chevron
@@ -75,7 +96,7 @@
                 setOf(
                     QSTileState.UserAction.CLICK,
                     QSTileState.UserAction.TOGGLE_CLICK,
-                    QSTileState.UserAction.LONG_CLICK
+                    QSTileState.UserAction.LONG_CLICK,
                 )
         }
 }
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 204ead3..6fe3979 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
@@ -20,13 +20,10 @@
 import android.content.Context
 import android.os.UserHandle
 import android.text.Html
-import com.android.settingslib.graph.SignalDrawable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
@@ -36,12 +33,12 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.utils.coroutines.flow.mapLatestConflated
 import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -51,7 +48,6 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
 
 @OptIn(ExperimentalCoroutinesApi::class)
 /** Observes internet state changes providing the [InternetTileModel]. */
@@ -59,7 +55,6 @@
 @Inject
 constructor(
     private val context: Context,
-    @Main private val mainCoroutineContext: CoroutineContext,
     @Application private val scope: CoroutineScope,
     airplaneModeRepository: AirplaneModeRepository,
     private val connectivityRepository: ConnectivityRepository,
@@ -79,8 +74,7 @@
                 flowOf(
                     InternetTileModel.Active(
                         secondaryTitle = secondary,
-                        iconId = wifiIcon.icon.res,
-                        icon = Icon.Loaded(context.getDrawable(wifiIcon.icon.res)!!, null),
+                        icon = InternetTileIconModel.ResourceId(wifiIcon.icon.res),
                         stateDescription = wifiIcon.contentDescription,
                         contentDescription = ContentDescription.Loaded("$internetLabel,$secondary"),
                     )
@@ -116,11 +110,10 @@
             if (it == null) {
                 notConnectedFlow
             } else {
-                combine(
-                        it.networkName,
-                        it.signalLevelIcon,
-                        mobileDataContentName,
-                    ) { networkNameModel, signalIcon, dataContentDescription ->
+                combine(it.networkName, it.signalLevelIcon, mobileDataContentName) {
+                        networkNameModel,
+                        signalIcon,
+                        dataContentDescription ->
                         Triple(networkNameModel, signalIcon, dataContentDescription)
                     }
                     .mapLatestConflated { (networkNameModel, signalIcon, dataContentDescription) ->
@@ -129,17 +122,12 @@
                                 val secondary =
                                     mobileDataContentConcat(
                                         networkNameModel.name,
-                                        dataContentDescription
+                                        dataContentDescription,
                                     )
 
-                                val drawable =
-                                    withContext(mainCoroutineContext) { SignalDrawable(context) }
-                                drawable.setLevel(signalIcon.level)
-                                val loadedIcon = Icon.Loaded(drawable, null)
-
                                 InternetTileModel.Active(
                                     secondaryTitle = secondary,
-                                    icon = loadedIcon,
+                                    icon = InternetTileIconModel.Cellular(signalIcon.level),
                                     stateDescription =
                                         ContentDescription.Loaded(secondary.toString()),
                                     contentDescription = ContentDescription.Loaded(internetLabel),
@@ -150,9 +138,10 @@
                                     signalIcon.icon.contentDescription.loadContentDescription(
                                         context
                                     )
+
                                 InternetTileModel.Active(
                                     secondaryTitle = secondary,
-                                    iconId = signalIcon.icon.res,
+                                    icon = InternetTileIconModel.Satellite(signalIcon.icon),
                                     stateDescription = ContentDescription.Loaded(secondary),
                                     contentDescription = ContentDescription.Loaded(internetLabel),
                                 )
@@ -164,7 +153,7 @@
 
     private fun mobileDataContentConcat(
         networkName: String?,
-        dataContentDescription: CharSequence?
+        dataContentDescription: CharSequence?,
     ): CharSequence {
         if (dataContentDescription == null) {
             return networkName ?: ""
@@ -177,9 +166,9 @@
             context.getString(
                 R.string.mobile_carrier_text_format,
                 networkName,
-                dataContentDescription
+                dataContentDescription,
             ),
-            0
+            0,
         )
     }
 
@@ -199,7 +188,7 @@
                 flowOf(
                     InternetTileModel.Active(
                         secondaryLabel = secondary?.toText(),
-                        iconId = it.res,
+                        icon = InternetTileIconModel.ResourceId(it.res),
                         stateDescription = null,
                         contentDescription = secondary,
                     )
@@ -208,16 +197,18 @@
         }
 
     private val notConnectedFlow: StateFlow<InternetTileModel> =
-        combine(
-                wifiInteractor.areNetworksAvailable,
-                airplaneModeRepository.isAirplaneMode,
-            ) { networksAvailable, isAirplaneMode ->
+        combine(wifiInteractor.areNetworksAvailable, airplaneModeRepository.isAirplaneMode) {
+                networksAvailable,
+                isAirplaneMode ->
                 when {
                     isAirplaneMode -> {
                         val secondary = context.getString(R.string.status_bar_airplane)
                         InternetTileModel.Inactive(
                             secondaryTitle = secondary,
-                            iconId = R.drawable.ic_qs_no_internet_unavailable,
+                            icon =
+                                InternetTileIconModel.ResourceId(
+                                    R.drawable.ic_qs_no_internet_unavailable
+                                ),
                             stateDescription = null,
                             contentDescription = ContentDescription.Loaded(secondary),
                         )
@@ -227,10 +218,13 @@
                             context.getString(R.string.quick_settings_networks_available)
                         InternetTileModel.Inactive(
                             secondaryTitle = secondary,
-                            iconId = R.drawable.ic_qs_no_internet_available,
+                            icon =
+                                InternetTileIconModel.ResourceId(
+                                    R.drawable.ic_qs_no_internet_available
+                                ),
                             stateDescription = null,
                             contentDescription =
-                                ContentDescription.Loaded("$internetLabel,$secondary")
+                                ContentDescription.Loaded("$internetLabel,$secondary"),
                         )
                     }
                     else -> {
@@ -248,7 +242,7 @@
      */
     override fun tileData(
         user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
+        triggers: Flow<DataUpdateTrigger>,
     ): Flow<InternetTileModel> =
         connectivityRepository.defaultConnections.flatMapLatest {
             when {
@@ -265,7 +259,7 @@
         val NOT_CONNECTED_NETWORKS_UNAVAILABLE =
             InternetTileModel.Inactive(
                 secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable),
-                iconId = R.drawable.ic_qs_no_internet_unavailable,
+                icon = InternetTileIconModel.ResourceId(R.drawable.ic_qs_no_internet_unavailable),
                 stateDescription = null,
                 contentDescription =
                     ContentDescription.Resource(R.string.quick_settings_networks_unavailable),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt
index ece90461..15b4e47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/model/InternetTileModel.kt
@@ -17,23 +17,21 @@
 package com.android.systemui.qs.tiles.impl.internet.domain.model
 
 import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
 
 /** Model describing the state that the QS Internet tile should be in. */
 sealed interface InternetTileModel {
     val secondaryTitle: CharSequence?
     val secondaryLabel: Text?
-    val iconId: Int?
-    val icon: Icon?
+    val icon: InternetTileIconModel
     val stateDescription: ContentDescription?
     val contentDescription: ContentDescription?
 
     data class Active(
         override val secondaryTitle: CharSequence? = null,
         override val secondaryLabel: Text? = null,
-        override val iconId: Int? = null,
-        override val icon: Icon? = null,
+        override val icon: InternetTileIconModel = InternetTileIconModel.Cellular(1),
         override val stateDescription: ContentDescription? = null,
         override val contentDescription: ContentDescription? = null,
     ) : InternetTileModel
@@ -41,8 +39,7 @@
     data class Inactive(
         override val secondaryTitle: CharSequence? = null,
         override val secondaryLabel: Text? = null,
-        override val iconId: Int? = null,
-        override val icon: Icon? = null,
+        override val icon: InternetTileIconModel = InternetTileIconModel.Cellular(1),
         override val stateDescription: ContentDescription? = null,
         override val contentDescription: ContentDescription? = null,
     ) : InternetTileModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
index cca947f..ac75932 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.impl.location.domain.interactor
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.content.Intent
 import android.provider.Settings
 import com.android.systemui.dagger.qualifiers.Application
@@ -52,7 +53,7 @@
                     val wasEnabled: Boolean = input.data.isEnabled
                     if (keyguardController.isMethodSecure() && keyguardController.isShowing()) {
                         activityStarter.postQSRunnableDismissingKeyguard {
-                            CoroutineScope(applicationScope.coroutineContext).launch {
+                            CoroutineScope(applicationScope.coroutineContext + createCoroutineTracingContext("LocationTileScope")).launch {
                                 locationController.setLocationEnabled(!wasEnabled)
                             }
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
index b25c61c..468e180 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.impl.saver.domain
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.content.Context
 import android.content.DialogInterface
 import android.content.SharedPreferences
@@ -44,7 +45,7 @@
             setTitle(R.string.data_saver_enable_title)
             setMessage(R.string.data_saver_description)
             setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ ->
-                CoroutineScope(backgroundContext).launch {
+                CoroutineScope(backgroundContext + createCoroutineTracingContext("DataSaverDialogScope")).launch {
                     dataSaverController.setDataSaverEnabled(true)
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
index 3b97d82..7c8fbea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
@@ -16,8 +16,7 @@
 
 package com.android.systemui.qs.ui.viewmodel
 
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -31,15 +30,12 @@
 class QuickSettingsShadeOverlayContentViewModel
 @AssistedInject
 constructor(
-    val sceneInteractor: SceneInteractor,
+    val shadeInteractor: ShadeInteractor,
     val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
     val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
 ) {
     fun onScrimClicked() {
-        sceneInteractor.hideOverlay(
-            overlay = Overlays.QuickSettingsShade,
-            loggingReason = "Shade scrim clicked",
-        )
+        shadeInteractor.collapseQuickSettingsShade(loggingReason = "Shade scrim clicked")
     }
 
     @AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ac49e91..559c263 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -248,7 +248,8 @@
                         } else if (action == ACTION_UP) {
                             // Gesture was too short to be picked up by scene container touch
                             // handling; programmatically start the transition to the shade.
-                            mShadeInteractor.get().expandNotificationShade("short launcher swipe");
+                            mShadeInteractor.get()
+                                    .expandNotificationsShade("short launcher swipe", null);
                         }
                     }
                     event.recycle();
@@ -265,7 +266,8 @@
                         mSceneInteractor.get().onRemoteUserInputStarted(
                                 "trackpad swipe");
                     } else if (action == ACTION_UP) {
-                        mShadeInteractor.get().expandNotificationShade("short trackpad swipe");
+                        mShadeInteractor.get()
+                                .expandNotificationsShade("short trackpad swipe", null);
                     }
                     mStatusBarWinController.getWindowRootView().dispatchTouchEvent(event);
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepository.kt
new file mode 100644
index 0000000..a8d0777
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepository.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.data.repository
+
+import android.graphics.Region
+import android.view.ISystemGestureExclusionListener
+import android.view.IWindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class SystemGestureExclusionRepository
+@Inject
+constructor(private val windowManager: IWindowManager) {
+
+    /**
+     * Returns [Flow] of the [Region] in which system gestures should be excluded on the display
+     * identified with [displayId].
+     */
+    fun exclusionRegion(displayId: Int): Flow<Region?> {
+        return conflatedCallbackFlow {
+            val listener =
+                object : ISystemGestureExclusionListener.Stub() {
+                    override fun onSystemGestureExclusionChanged(
+                        displayId: Int,
+                        restrictedRegion: Region?,
+                        unrestrictedRegion: Region?,
+                    ) {
+                        trySend(restrictedRegion)
+                    }
+                }
+            windowManager.registerSystemGestureExclusionListener(listener, displayId)
+
+            awaitClose {
+                windowManager.unregisterSystemGestureExclusionListener(listener, displayId)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractor.kt
new file mode 100644
index 0000000..4cee874
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractor.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.domain.interactor
+
+import android.graphics.Region
+import com.android.systemui.scene.data.repository.SystemGestureExclusionRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+class SystemGestureExclusionInteractor
+@Inject
+constructor(private val repository: SystemGestureExclusionRepository) {
+
+    /**
+     * Returns [Flow] of the [Region] in which system gestures should be excluded on the display
+     * identified with [displayId].
+     */
+    fun exclusionRegion(displayId: Int): Flow<Region?> {
+        return repository.exclusionRegion(displayId)
+    }
+}
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 b7e2cf2..286cac1 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
@@ -193,22 +193,16 @@
                         // We are in a session if either Shade or QuickSettings is on the back stack
                         .map { backStack ->
                             backStack.asIterable().any {
+                                // TODO(b/356596436): Include overlays in the back stack as well.
                                 it == Scenes.Shade || it == Scenes.QuickSettings
                             }
                         }
                         .distinctUntilChanged(),
-                    sceneInteractor.transitionState
-                        .mapNotNull { state ->
-                            // We are also in a session if either Shade or QuickSettings is the
-                            // current scene
-                            when (state) {
-                                is ObservableTransitionState.Idle -> state.currentScene
-                                is ObservableTransitionState.Transition -> state.fromContent
-                            }.let { it == Scenes.Shade || it == Scenes.QuickSettings }
-                        }
-                        .distinctUntilChanged(),
-                ) { inBackStack, isCurrentScene ->
-                    inBackStack || isCurrentScene
+                    // We are also in a session if either Notifications Shade or QuickSettings Shade
+                    // is currently shown (whether idle or animating).
+                    shadeInteractor.isAnyExpanded,
+                ) { inBackStack, isShadeShown ->
+                    inBackStack || isShadeShown
                 }
                 // Once a session has ended, clear the session storage.
                 .filter { inSession -> !inSession }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
index b9f57f2..3c6d858 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
@@ -32,4 +32,7 @@
      * normal collapse would.
      */
     val SlightlyFasterShadeCollapse = TransitionKey("SlightlyFasterShadeCollapse")
+
+    /** Reference to a content transition that should happen instantly, i.e. without animation. */
+    val Instant = TransitionKey("Instant")
 }
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 7f35d73..a7e7d8b 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
@@ -107,7 +107,9 @@
             view.viewModel(
                 traceName = "SceneWindowRootViewBinder",
                 minWindowLifecycleState = WindowLifecycleState.ATTACHED,
-                factory = { viewModelFactory.create(motionEventHandlerReceiver) },
+                factory = {
+                    viewModelFactory.create(view.context.displayId, motionEventHandlerReceiver)
+                },
             ) { viewModel ->
                 try {
                     view.setViewTreeOnBackPressedDispatcherOwner(
@@ -184,7 +186,7 @@
                 PlatformTheme {
                     ScreenDecorProvider(
                         displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
-                        screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
+                        screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context),
                     ) {
                         SceneContainer(
                             viewModel = viewModel,
@@ -205,9 +207,7 @@
     ): ComposeView {
         return ComposeView(context).apply {
             setContent {
-                AlternateBouncer(
-                    alternateBouncerDependencies = alternateBouncerDependencies,
-                )
+                AlternateBouncer(alternateBouncerDependencies = alternateBouncerDependencies)
             }
         }
     }
@@ -234,14 +234,7 @@
                         else -> CutoutLocation.CENTER
                     }
                 val viewDisplayCutout = it?.displayCutout
-                DisplayCutout(
-                    left,
-                    top,
-                    right,
-                    bottom,
-                    location,
-                    viewDisplayCutout,
-                )
+                DisplayCutout(left, top, right, bottom, location, viewDisplayCutout)
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), DisplayCutout())
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilter.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilter.kt
new file mode 100644
index 0000000..a1d915a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerGestureFilter.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.viewmodel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.geometry.Offset
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.scene.domain.interactor.SystemGestureExclusionInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.math.roundToInt
+
+/** Decides whether drag gestures should be filtered out in the scene container framework. */
+class SceneContainerGestureFilter
+@AssistedInject
+constructor(interactor: SystemGestureExclusionInteractor, @Assisted displayId: Int) :
+    ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("SceneContainerGestureFilter.hydrator")
+    private val exclusionRegion by
+        hydrator.hydratedStateOf(
+            traceName = "exclusionRegion",
+            initialValue = null,
+            source = interactor.exclusionRegion(displayId),
+        )
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
+
+    /**
+     * Returns `true` if a drag gesture starting at [startPosition] should be filtered out (e.g.
+     * ignored, `false` otherwise.
+     *
+     * Invoke this and pass in the position of the `ACTION_DOWN` pointer event that began the
+     * gesture.
+     */
+    fun shouldFilterGesture(startPosition: Offset): Boolean {
+        check(isActive) { "Must be activated to use!" }
+
+        return exclusionRegion?.contains(startPosition.x.roundToInt(), startPosition.y.roundToInt())
+            ?: false
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(displayId: Int): SceneContainerGestureFilter
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index af1f5a7..0bf2d49 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -18,6 +18,7 @@
 
 import android.view.MotionEvent
 import androidx.compose.runtime.getValue
+import androidx.compose.ui.geometry.Offset
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.DefaultEdgeDetector
 import com.android.compose.animation.scene.ObservableTransitionState
@@ -41,9 +42,12 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
 
 /** Models UI state for the scene container. */
 class SceneContainerViewModel
@@ -55,6 +59,8 @@
     shadeInteractor: ShadeInteractor,
     private val splitEdgeDetector: SplitEdgeDetector,
     private val logger: SceneLogger,
+    gestureFilterFactory: SceneContainerGestureFilter.Factory,
+    @Assisted displayId: Int,
     @Assisted private val motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
 ) : ExclusiveActivatable() {
 
@@ -80,6 +86,8 @@
                 },
         )
 
+    private val gestureFilter: SceneContainerGestureFilter = gestureFilterFactory.create(displayId)
+
     override suspend fun onActivated(): Nothing {
         try {
             // Sends a MotionEventHandler to the owner of the view-model so they can report
@@ -96,7 +104,11 @@
                 }
             )
 
-            hydrator.activate()
+            coroutineScope {
+                launch { hydrator.activate() }
+                launch { gestureFilter.activate() }
+            }
+            awaitCancellation()
         } finally {
             // Clears the previously-sent MotionEventHandler so the owner of the view-model releases
             // their reference to it.
@@ -243,6 +255,17 @@
         }
     }
 
+    /**
+     * Returns `true` if a drag gesture starting at [startPosition] should be filtered out (e.g.
+     * ignored, `false` otherwise.
+     *
+     * Invoke this and pass in the position of the `ACTION_DOWN` pointer event that began the
+     * gesture.
+     */
+    fun shouldFilterGesture(startPosition: Offset): Boolean {
+        return gestureFilter.shouldFilterGesture(startPosition)
+    }
+
     /** Defines interface for classes that can handle externally-reported [MotionEvent]s. */
     interface MotionEventHandler {
         /** Notifies that a [MotionEvent] has occurred. */
@@ -258,7 +281,8 @@
     @AssistedFactory
     interface Factory {
         fun create(
-            motionEventHandlerReceiver: (MotionEventHandler?) -> Unit
+            displayId: Int,
+            motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
         ): SceneContainerViewModel
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
index f69b0cb..7724abd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
@@ -505,8 +505,8 @@
             return;
         }
         // delay starting scroll capture to make sure scrim is up before the app moves
-        mViewProxy.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
-                mScreenshotTakenInPortrait, () -> executeBatchScrollCapture(response, owner));
+        mViewProxy.prepareScrollingTransition(response, newScreenshot, mScreenshotTakenInPortrait,
+                () -> executeBatchScrollCapture(response, owner));
     }
 
     private void executeBatchScrollCapture(ScrollCaptureResponse response, UserHandle owner) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
deleted file mode 100644
index fe58bc9..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ /dev/null
@@ -1,663 +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.screenshot;
-
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-
-import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_UI;
-import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW;
-import static com.android.systemui.screenshot.LogConfig.logTag;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Display;
-import android.view.ScrollCaptureResponse;
-import android.view.ViewRootImpl;
-import android.view.WindowManager;
-import android.widget.Toast;
-import android.window.WindowContext;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.clipboardoverlay.ClipboardOverlayController;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.res.R;
-import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
-import com.android.systemui.screenshot.scroll.ScrollCaptureExecutor;
-import com.android.systemui.util.Assert;
-
-import com.google.common.util.concurrent.ListenableFuture;
-
-import dagger.assisted.Assisted;
-import dagger.assisted.AssistedFactory;
-import dagger.assisted.AssistedInject;
-
-import kotlin.Unit;
-
-import java.util.UUID;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.function.Consumer;
-
-import javax.inject.Provider;
-
-/**
- * Controls the state and flow for screenshots.
- */
-public class ScreenshotController implements InteractiveScreenshotHandler {
-    private static final String TAG = logTag(ScreenshotController.class);
-
-    // From WizardManagerHelper.java
-    private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
-
-    static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000;
-
-    private final WindowContext mContext;
-    private final FeatureFlags mFlags;
-    private final ScreenshotShelfViewProxy mViewProxy;
-    private final ScreenshotNotificationsController mNotificationsController;
-    private final ScreenshotSmartActions mScreenshotSmartActions;
-    private final UiEventLogger mUiEventLogger;
-    private final ImageExporter mImageExporter;
-    private final ImageCapture mImageCapture;
-    private final Executor mMainExecutor;
-    private final ExecutorService mBgExecutor;
-    private final BroadcastSender mBroadcastSender;
-    private final BroadcastDispatcher mBroadcastDispatcher;
-    private final ScreenshotActionsController mActionsController;
-
-    @Nullable
-    private final ScreenshotSoundController mScreenshotSoundController;
-    private final ScreenshotWindow mWindow;
-    private final Display mDisplay;
-    private final ScrollCaptureExecutor mScrollCaptureExecutor;
-    private final ScreenshotNotificationSmartActionsProvider
-            mScreenshotNotificationSmartActionsProvider;
-    private final TimeoutHandler mScreenshotHandler;
-    private final UserManager mUserManager;
-    private final AssistContentRequester mAssistContentRequester;
-    private final ActionExecutor mActionExecutor;
-
-
-    private final MessageContainerController mMessageContainerController;
-    private final AnnouncementResolver mAnnouncementResolver;
-    private Bitmap mScreenBitmap;
-    private boolean mScreenshotTakenInPortrait;
-    private Animator mScreenshotAnimation;
-    private RequestCallback mCurrentRequestCallback;
-    private String mPackageName = "";
-    private final BroadcastReceiver mCopyBroadcastReceiver;
-
-    /** Tracks config changes that require re-creating UI */
-    private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
-            ActivityInfo.CONFIG_ORIENTATION
-                    | ActivityInfo.CONFIG_LAYOUT_DIRECTION
-                    | ActivityInfo.CONFIG_LOCALE
-                    | ActivityInfo.CONFIG_UI_MODE
-                    | ActivityInfo.CONFIG_SCREEN_LAYOUT
-                    | ActivityInfo.CONFIG_ASSETS_PATHS);
-
-
-    @AssistedInject
-    ScreenshotController(
-            Context context,
-            ScreenshotWindow.Factory screenshotWindowFactory,
-            FeatureFlags flags,
-            ScreenshotShelfViewProxy.Factory viewProxyFactory,
-            ScreenshotSmartActions screenshotSmartActions,
-            ScreenshotNotificationsController.Factory screenshotNotificationsControllerFactory,
-            UiEventLogger uiEventLogger,
-            ImageExporter imageExporter,
-            ImageCapture imageCapture,
-            @Main Executor mainExecutor,
-            ScrollCaptureExecutor scrollCaptureExecutor,
-            TimeoutHandler timeoutHandler,
-            BroadcastSender broadcastSender,
-            BroadcastDispatcher broadcastDispatcher,
-            ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
-            ScreenshotActionsController.Factory screenshotActionsControllerFactory,
-            ActionExecutor.Factory actionExecutorFactory,
-            UserManager userManager,
-            AssistContentRequester assistContentRequester,
-            MessageContainerController messageContainerController,
-            Provider<ScreenshotSoundController> screenshotSoundController,
-            AnnouncementResolver announcementResolver,
-            @Assisted Display display
-    ) {
-        mScreenshotSmartActions = screenshotSmartActions;
-        mNotificationsController = screenshotNotificationsControllerFactory.create(
-                display.getDisplayId());
-        mUiEventLogger = uiEventLogger;
-        mImageExporter = imageExporter;
-        mImageCapture = imageCapture;
-        mMainExecutor = mainExecutor;
-        mScrollCaptureExecutor = scrollCaptureExecutor;
-        mScreenshotNotificationSmartActionsProvider = screenshotNotificationSmartActionsProvider;
-        mBgExecutor = Executors.newSingleThreadExecutor();
-        mBroadcastSender = broadcastSender;
-        mBroadcastDispatcher = broadcastDispatcher;
-
-        mScreenshotHandler = timeoutHandler;
-        mScreenshotHandler.setDefaultTimeoutMillis(SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS);
-
-        mDisplay = display;
-        mWindow = screenshotWindowFactory.create(mDisplay);
-        mContext = mWindow.getContext();
-        mFlags = flags;
-        mUserManager = userManager;
-        mMessageContainerController = messageContainerController;
-        mAssistContentRequester = assistContentRequester;
-        mAnnouncementResolver = announcementResolver;
-
-        mViewProxy = viewProxyFactory.getProxy(mContext, mDisplay.getDisplayId());
-
-        mScreenshotHandler.setOnTimeoutRunnable(() -> {
-            if (DEBUG_UI) {
-                Log.d(TAG, "Corner timeout hit");
-            }
-            mViewProxy.requestDismissal(SCREENSHOT_INTERACTION_TIMEOUT);
-        });
-
-        mConfigChanges.applyNewConfig(context.getResources());
-        reloadAssets();
-
-        mActionExecutor = actionExecutorFactory.create(mWindow.getWindow(), mViewProxy,
-                () -> {
-                    finishDismiss();
-                    return Unit.INSTANCE;
-                });
-        mActionsController = screenshotActionsControllerFactory.getController(mActionExecutor);
-
-
-        // Sound is only reproduced from the controller of the default display.
-        if (mDisplay.getDisplayId() == Display.DEFAULT_DISPLAY) {
-            mScreenshotSoundController = screenshotSoundController.get();
-        } else {
-            mScreenshotSoundController = null;
-        }
-
-        mCopyBroadcastReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (ClipboardOverlayController.COPY_OVERLAY_ACTION.equals(intent.getAction())) {
-                    mViewProxy.requestDismissal(SCREENSHOT_DISMISSED_OTHER);
-                }
-            }
-        };
-        mBroadcastDispatcher.registerReceiver(mCopyBroadcastReceiver, new IntentFilter(
-                        ClipboardOverlayController.COPY_OVERLAY_ACTION), null, null,
-                Context.RECEIVER_NOT_EXPORTED, ClipboardOverlayController.SELF_PERMISSION);
-    }
-
-    @Override
-    public void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
-            RequestCallback requestCallback) {
-        Assert.isMainThread();
-
-        mCurrentRequestCallback = requestCallback;
-        if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-                && screenshot.getBitmap() == null) {
-            Rect bounds = getFullScreenRect();
-            screenshot.setBitmap(mImageCapture.captureDisplay(mDisplay.getDisplayId(), bounds));
-            screenshot.setScreenBounds(bounds);
-        }
-
-        if (screenshot.getBitmap() == null) {
-            Log.e(TAG, "handleScreenshot: Screenshot bitmap was null");
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-            if (mCurrentRequestCallback != null) {
-                mCurrentRequestCallback.reportError();
-            }
-            return;
-        }
-
-        mScreenBitmap = screenshot.getBitmap();
-        String oldPackageName = mPackageName;
-        mPackageName = screenshot.getPackageNameString();
-
-        if (!isUserSetupComplete(Process.myUserHandle())) {
-            Log.w(TAG, "User setup not complete, displaying toast only");
-            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
-            // and sharing shouldn't be exposed to the user.
-            saveScreenshotAndToast(screenshot, finisher);
-            return;
-        }
-
-        mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
-                ClipboardOverlayController.SELF_PERMISSION);
-
-        mScreenshotTakenInPortrait =
-                mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
-
-        // Optimizations
-        mScreenBitmap.setHasAlpha(false);
-        mScreenBitmap.prepareToDraw();
-
-        prepareViewForNewScreenshot(screenshot, oldPackageName);
-
-        final UUID requestId;
-        requestId = mActionsController.setCurrentScreenshot(screenshot);
-        saveScreenshotInBackground(screenshot, requestId, finisher, result -> {
-            if (result.uri != null) {
-                ScreenshotSavedResult savedScreenshot = new ScreenshotSavedResult(
-                        result.uri, screenshot.getUserOrDefault(), result.timestamp);
-                mActionsController.setCompletedScreenshot(requestId, savedScreenshot);
-            }
-        });
-
-        if (screenshot.getTaskId() >= 0) {
-            mAssistContentRequester.requestAssistContent(
-                    screenshot.getTaskId(),
-                    assistContent ->
-                            mActionsController.onAssistContent(requestId, assistContent));
-        } else {
-            mActionsController.onAssistContent(requestId, null);
-        }
-
-        // The window is focusable by default
-        mWindow.setFocusable(true);
-        mViewProxy.requestFocus();
-
-        enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
-
-        mWindow.attachWindow();
-
-        boolean showFlash;
-        if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
-            if (screenshot.getScreenBounds() != null
-                    && aspectRatiosMatch(screenshot.getBitmap(), screenshot.getInsets(),
-                    screenshot.getScreenBounds())) {
-                showFlash = false;
-            } else {
-                showFlash = true;
-                screenshot.setInsets(Insets.NONE);
-                screenshot.setScreenBounds(new Rect(0, 0, screenshot.getBitmap().getWidth(),
-                        screenshot.getBitmap().getHeight()));
-            }
-        } else {
-            showFlash = true;
-        }
-
-        mViewProxy.prepareEntranceAnimation(
-                () -> startAnimation(screenshot.getScreenBounds(), showFlash,
-                        () -> mMessageContainerController.onScreenshotTaken(screenshot)));
-
-        mViewProxy.setScreenshot(screenshot);
-
-    }
-
-    void prepareViewForNewScreenshot(@NonNull ScreenshotData screenshot, String oldPackageName) {
-        mWindow.whenWindowAttached(() -> {
-            mAnnouncementResolver.getScreenshotAnnouncement(
-                    screenshot.getUserHandle().getIdentifier(),
-                    announcement -> {
-                        mViewProxy.announceForAccessibility(announcement);
-                    });
-        });
-
-        mViewProxy.reset();
-
-        if (mViewProxy.isAttachedToWindow()) {
-            // if we didn't already dismiss for another reason
-            if (!mViewProxy.isDismissing()) {
-                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0,
-                        oldPackageName);
-            }
-            if (DEBUG_WINDOW) {
-                Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
-                        + "(dismissing=" + mViewProxy.isDismissing() + ")");
-            }
-        }
-
-        mViewProxy.setPackageName(mPackageName);
-    }
-
-    /**
-     * Requests the view to dismiss the current screenshot (may be ignored, if screenshot is already
-     * being dismissed)
-     */
-    @Override
-    public void requestDismissal(ScreenshotEvent event) {
-        mViewProxy.requestDismissal(event);
-    }
-
-    @Override
-    public boolean isPendingSharedTransition() {
-        return mActionExecutor.isPendingSharedTransition();
-    }
-
-    // Any cleanup needed when the service is being destroyed.
-    @Override
-    public void onDestroy() {
-        removeWindow();
-        releaseMediaPlayer();
-        releaseContext();
-        mBgExecutor.shutdown();
-    }
-
-    /**
-     * Release the constructed window context.
-     */
-    private void releaseContext() {
-        mBroadcastDispatcher.unregisterReceiver(mCopyBroadcastReceiver);
-        mContext.release();
-    }
-
-    private void releaseMediaPlayer() {
-        if (mScreenshotSoundController == null) return;
-        mScreenshotSoundController.releaseScreenshotSoundAsync();
-    }
-
-    /**
-     * Update resources on configuration change. Reinflate for theme/color changes.
-     */
-    private void reloadAssets() {
-        if (DEBUG_UI) {
-            Log.d(TAG, "reloadAssets()");
-        }
-
-        mMessageContainerController.setView(mViewProxy.getView());
-        mViewProxy.setCallbacks(new ScreenshotShelfViewProxy.ScreenshotViewCallback() {
-            @Override
-            public void onUserInteraction() {
-                if (DEBUG_INPUT) {
-                    Log.d(TAG, "onUserInteraction");
-                }
-                mScreenshotHandler.resetTimeout();
-            }
-
-            @Override
-            public void onDismiss() {
-                finishDismiss();
-            }
-
-            @Override
-            public void onTouchOutside() {
-                // TODO(159460485): Remove this when focus is handled properly in the system
-                mWindow.setFocusable(false);
-            }
-        });
-
-        if (DEBUG_WINDOW) {
-            Log.d(TAG, "setContentView: " + mViewProxy.getView());
-        }
-        mWindow.setContentView(mViewProxy.getView());
-    }
-
-    private void enqueueScrollCaptureRequest(UUID requestId, UserHandle owner) {
-        // Wait until this window is attached to request because it is
-        // the reference used to locate the target window (below).
-        mWindow.whenWindowAttached(() -> {
-            requestScrollCapture(requestId, owner);
-            mWindow.setActivityConfigCallback(
-                    new ViewRootImpl.ActivityConfigCallback() {
-                        @Override
-                        public void onConfigurationChanged(Configuration overrideConfig,
-                                int newDisplayId) {
-                            if (mConfigChanges.applyNewConfig(mContext.getResources())) {
-                                // Hide the scroll chip until we know it's available in this
-                                // orientation
-                                mActionsController.onScrollChipInvalidated();
-                                // Delay scroll capture eval a bit to allow the underlying activity
-                                // to set up in the new orientation.
-                                mScreenshotHandler.postDelayed(
-                                        () -> requestScrollCapture(requestId, owner), 150);
-                                mViewProxy.updateInsets(mWindow.getWindowInsets());
-                                // Screenshot animation calculations won't be valid anymore,
-                                // so just end
-                                if (mScreenshotAnimation != null
-                                        && mScreenshotAnimation.isRunning()) {
-                                    mScreenshotAnimation.end();
-                                }
-                            }
-                        }
-                    });
-        });
-    }
-
-    private void requestScrollCapture(UUID requestId, UserHandle owner) {
-        mScrollCaptureExecutor.requestScrollCapture(
-                mDisplay.getDisplayId(),
-                mWindow.getWindowToken(),
-                (response) -> {
-                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
-                            0, response.getPackageName());
-                    mActionsController.onScrollChipReady(requestId,
-                            () -> onScrollButtonClicked(owner, response));
-                    return Unit.INSTANCE;
-                }
-        );
-    }
-
-    private void onScrollButtonClicked(UserHandle owner, ScrollCaptureResponse response) {
-        if (DEBUG_INPUT) {
-            Log.d(TAG, "scroll chip tapped");
-        }
-        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED, 0,
-                response.getPackageName());
-        Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplay.getDisplayId(),
-                getFullScreenRect());
-        if (newScreenshot == null) {
-            Log.e(TAG, "Failed to capture current screenshot for scroll transition!");
-            return;
-        }
-        // delay starting scroll capture to make sure scrim is up before the app moves
-        mViewProxy.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
-                mScreenshotTakenInPortrait, () -> executeBatchScrollCapture(response, owner));
-    }
-
-    private void executeBatchScrollCapture(ScrollCaptureResponse response, UserHandle owner) {
-        mScrollCaptureExecutor.executeBatchScrollCapture(response,
-                () -> {
-                    final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent(
-                            owner, mContext);
-                    mContext.startActivity(intent);
-                },
-                mViewProxy::restoreNonScrollingUi,
-                mViewProxy::startLongScreenshotTransition);
-    }
-
-    @Override
-    public void removeWindow() {
-        mWindow.removeWindow();
-        mViewProxy.stopInputListening();
-    }
-
-    private void playCameraSoundIfNeeded() {
-        if (mScreenshotSoundController == null) return;
-        // the controller is not-null only on the default display controller
-        mScreenshotSoundController.playScreenshotSoundAsync();
-    }
-
-    /**
-     * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
-     * failure).
-     */
-    private void saveScreenshotAndToast(ScreenshotData screenshot, Consumer<Uri> finisher) {
-        // Play the shutter sound to notify that we've taken a screenshot
-        playCameraSoundIfNeeded();
-
-        saveScreenshotInBackground(screenshot, UUID.randomUUID(), finisher, result -> {
-            if (result.uri != null) {
-                mScreenshotHandler.post(() -> Toast.makeText(mContext,
-                        R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
-            }
-        });
-    }
-
-    /**
-     * Starts the animation after taking the screenshot
-     */
-    private void startAnimation(Rect screenRect, boolean showFlash, Runnable onAnimationComplete) {
-        if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
-            mScreenshotAnimation.cancel();
-        }
-
-        mScreenshotAnimation =
-                mViewProxy.createScreenshotDropInAnimation(screenRect, showFlash);
-        if (onAnimationComplete != null) {
-            mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    onAnimationComplete.run();
-                }
-            });
-        }
-
-        // Play the shutter sound to notify that we've taken a screenshot
-        playCameraSoundIfNeeded();
-
-        if (DEBUG_ANIM) {
-            Log.d(TAG, "starting post-screenshot animation");
-        }
-        mScreenshotAnimation.start();
-    }
-
-    /** Reset screenshot view and then call onCompleteRunnable */
-    private void finishDismiss() {
-        Log.d(TAG, "finishDismiss");
-        mActionsController.endScreenshotSession();
-        mScrollCaptureExecutor.close();
-        if (mCurrentRequestCallback != null) {
-            mCurrentRequestCallback.onFinish();
-            mCurrentRequestCallback = null;
-        }
-        mViewProxy.reset();
-        removeWindow();
-        mScreenshotHandler.cancelTimeout();
-    }
-
-    private void saveScreenshotInBackground(ScreenshotData screenshot, UUID requestId,
-            Consumer<Uri> finisher, Consumer<ImageExporter.Result> onResult) {
-        ListenableFuture<ImageExporter.Result> future = mImageExporter.export(mBgExecutor,
-                requestId, screenshot.getBitmap(), screenshot.getUserOrDefault(),
-                mDisplay.getDisplayId());
-        future.addListener(() -> {
-            try {
-                ImageExporter.Result result = future.get();
-                Log.d(TAG, "Saved screenshot: " + result);
-                logScreenshotResultStatus(result.uri, screenshot.getUserHandle());
-                onResult.accept(result);
-                if (DEBUG_CALLBACK) {
-                    Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) "
-                            + "finisher.accept(\"" + result.uri + "\"");
-                }
-                finisher.accept(result.uri);
-            } catch (Exception e) {
-                Log.d(TAG, "Failed to store screenshot", e);
-                if (DEBUG_CALLBACK) {
-                    Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)");
-                }
-                finisher.accept(null);
-            }
-        }, mMainExecutor);
-    }
-
-    /**
-     * Logs success/failure of the screenshot saving task, and shows an error if it failed.
-     */
-    private void logScreenshotResultStatus(Uri uri, UserHandle owner) {
-        if (uri == null) {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, mPackageName);
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_save_text);
-        } else {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, mPackageName);
-            if (mUserManager.isManagedProfile(owner.getIdentifier())) {
-                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 0,
-                        mPackageName);
-            }
-        }
-    }
-
-    private boolean isUserSetupComplete(UserHandle owner) {
-        return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0)
-                .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
-    }
-
-    private Rect getFullScreenRect() {
-        DisplayMetrics displayMetrics = new DisplayMetrics();
-        mDisplay.getRealMetrics(displayMetrics);
-        return new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
-    }
-
-    /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
-    private static boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets,
-            Rect screenBounds) {
-        int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
-        int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom;
-
-        if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
-                || bitmap.getHeight() == 0) {
-            if (DEBUG_UI) {
-                Log.e(TAG, "Provided bitmap and insets create degenerate region: "
-                        + bitmap.getWidth() + "x" + bitmap.getHeight() + " " + bitmapInsets);
-            }
-            return false;
-        }
-
-        float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight;
-        float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
-
-        boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
-        if (DEBUG_UI) {
-            Log.d(TAG, "aspectRatiosMatch: don't match bitmap: " + insettedBitmapAspect
-                    + ", bounds: " + boundsAspect);
-        }
-        return matchWithinTolerance;
-    }
-
-    /** Injectable factory to create screenshot controller instances for a specific display. */
-    @AssistedFactory
-    public interface Factory extends InteractiveScreenshotHandler.Factory {
-        /**
-         * Creates an instance of the controller for that specific display.
-         *
-         * @param display                 display to capture
-         */
-        ScreenshotController create(Display display);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
new file mode 100644
index 0000000..29208f8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -0,0 +1,632 @@
+/*
+ * 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.screenshot
+
+import android.animation.Animator
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Insets
+import android.graphics.Rect
+import android.net.Uri
+import android.os.Process
+import android.os.UserHandle
+import android.os.UserManager
+import android.provider.Settings
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.Display
+import android.view.ScrollCaptureResponse
+import android.view.ViewRootImpl.ActivityConfigCallback
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import android.widget.Toast
+import android.window.WindowContext
+import androidx.core.animation.doOnEnd
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.applications.InterestingConfigChanges
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.clipboardoverlay.ClipboardOverlayController
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.android.systemui.screenshot.ActionIntentCreator.createLongScreenshotIntent
+import com.android.systemui.screenshot.ScreenshotShelfViewProxy.ScreenshotViewCallback
+import com.android.systemui.screenshot.scroll.ScrollCaptureController.LongScreenshot
+import com.android.systemui.screenshot.scroll.ScrollCaptureExecutor
+import com.android.systemui.util.Assert
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.UUID
+import java.util.concurrent.Executor
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.function.Consumer
+import javax.inject.Provider
+import kotlin.math.abs
+
+/** Controls the state and flow for screenshots. */
+class ScreenshotController
+@AssistedInject
+internal constructor(
+    appContext: Context,
+    screenshotWindowFactory: ScreenshotWindow.Factory,
+    viewProxyFactory: ScreenshotShelfViewProxy.Factory,
+    screenshotNotificationsControllerFactory: ScreenshotNotificationsController.Factory,
+    screenshotActionsControllerFactory: ScreenshotActionsController.Factory,
+    actionExecutorFactory: ActionExecutor.Factory,
+    screenshotSoundControllerProvider: Provider<ScreenshotSoundController?>,
+    private val uiEventLogger: UiEventLogger,
+    private val imageExporter: ImageExporter,
+    private val imageCapture: ImageCapture,
+    private val scrollCaptureExecutor: ScrollCaptureExecutor,
+    private val screenshotHandler: TimeoutHandler,
+    private val broadcastSender: BroadcastSender,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val userManager: UserManager,
+    private val assistContentRequester: AssistContentRequester,
+    private val messageContainerController: MessageContainerController,
+    private val announcementResolver: AnnouncementResolver,
+    @Main private val mainExecutor: Executor,
+    @Assisted private val display: Display,
+) : InteractiveScreenshotHandler {
+    private val context: WindowContext
+    private val viewProxy: ScreenshotShelfViewProxy
+    private val notificationController =
+        screenshotNotificationsControllerFactory.create(display.displayId)
+    private val bgExecutor: ExecutorService = Executors.newSingleThreadExecutor()
+    private val actionsController: ScreenshotActionsController
+    private val window: ScreenshotWindow
+    private val actionExecutor: ActionExecutor
+    private val copyBroadcastReceiver: BroadcastReceiver
+
+    private var screenshotSoundController: ScreenshotSoundController? = null
+    private var screenBitmap: Bitmap? = null
+    private var screenshotTakenInPortrait = false
+    private var screenshotAnimation: Animator? = null
+    private var currentRequestCallback: TakeScreenshotService.RequestCallback? = null
+    private var packageName = ""
+
+    /** Tracks config changes that require re-creating UI */
+    private val configChanges =
+        InterestingConfigChanges(
+            ActivityInfo.CONFIG_ORIENTATION or
+                ActivityInfo.CONFIG_LAYOUT_DIRECTION or
+                ActivityInfo.CONFIG_LOCALE or
+                ActivityInfo.CONFIG_UI_MODE or
+                ActivityInfo.CONFIG_SCREEN_LAYOUT or
+                ActivityInfo.CONFIG_ASSETS_PATHS
+        )
+
+    init {
+        screenshotHandler.defaultTimeoutMillis = SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS
+
+        window = screenshotWindowFactory.create(display)
+        context = window.getContext()
+
+        viewProxy = viewProxyFactory.getProxy(context, display.displayId)
+
+        screenshotHandler.setOnTimeoutRunnable {
+            if (LogConfig.DEBUG_UI) {
+                Log.d(TAG, "Corner timeout hit")
+            }
+            viewProxy.requestDismissal(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT)
+        }
+
+        configChanges.applyNewConfig(appContext.resources)
+        reloadAssets()
+
+        actionExecutor = actionExecutorFactory.create(window.window, viewProxy) { finishDismiss() }
+        actionsController = screenshotActionsControllerFactory.getController(actionExecutor)
+
+        // Sound is only reproduced from the controller of the default display.
+        screenshotSoundController =
+            if (display.displayId == Display.DEFAULT_DISPLAY) {
+                screenshotSoundControllerProvider.get()
+            } else {
+                null
+            }
+
+        copyBroadcastReceiver =
+            object : BroadcastReceiver() {
+                override fun onReceive(context: Context, intent: Intent) {
+                    if (ClipboardOverlayController.COPY_OVERLAY_ACTION == intent.action) {
+                        viewProxy.requestDismissal(ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER)
+                    }
+                }
+            }
+        broadcastDispatcher.registerReceiver(
+            copyBroadcastReceiver,
+            IntentFilter(ClipboardOverlayController.COPY_OVERLAY_ACTION),
+            null,
+            null,
+            Context.RECEIVER_NOT_EXPORTED,
+            ClipboardOverlayController.SELF_PERMISSION,
+        )
+    }
+
+    override fun handleScreenshot(
+        screenshot: ScreenshotData,
+        finisher: Consumer<Uri?>,
+        requestCallback: TakeScreenshotService.RequestCallback,
+    ) {
+        Assert.isMainThread()
+
+        currentRequestCallback = requestCallback
+        if (screenshot.type == TAKE_SCREENSHOT_FULLSCREEN && screenshot.bitmap == null) {
+            val bounds = fullScreenRect
+            screenshot.bitmap = imageCapture.captureDisplay(display.displayId, bounds)
+            screenshot.screenBounds = bounds
+        }
+
+        val currentBitmap = screenshot.bitmap
+        if (currentBitmap == null) {
+            Log.e(TAG, "handleScreenshot: Screenshot bitmap was null")
+            notificationController.notifyScreenshotError(R.string.screenshot_failed_to_capture_text)
+            currentRequestCallback?.reportError()
+            return
+        }
+
+        screenBitmap = currentBitmap
+        val oldPackageName = packageName
+        packageName = screenshot.packageNameString
+
+        if (!isUserSetupComplete(Process.myUserHandle())) {
+            Log.w(TAG, "User setup not complete, displaying toast only")
+            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+            // and sharing shouldn't be exposed to the user.
+            saveScreenshotAndToast(screenshot, finisher)
+            return
+        }
+
+        broadcastSender.sendBroadcast(
+            Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
+            ClipboardOverlayController.SELF_PERMISSION,
+        )
+
+        screenshotTakenInPortrait =
+            context.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
+
+        // Optimizations
+        currentBitmap.setHasAlpha(false)
+        currentBitmap.prepareToDraw()
+
+        prepareViewForNewScreenshot(screenshot, oldPackageName)
+        val requestId = actionsController.setCurrentScreenshot(screenshot)
+        saveScreenshotInBackground(screenshot, requestId, finisher) { result ->
+            if (result.uri != null) {
+                val savedScreenshot =
+                    ScreenshotSavedResult(
+                        result.uri,
+                        screenshot.getUserOrDefault(),
+                        result.timestamp,
+                    )
+                actionsController.setCompletedScreenshot(requestId, savedScreenshot)
+            }
+        }
+
+        if (screenshot.taskId >= 0) {
+            assistContentRequester.requestAssistContent(screenshot.taskId) { assistContent ->
+                actionsController.onAssistContent(requestId, assistContent)
+            }
+        } else {
+            actionsController.onAssistContent(requestId, null)
+        }
+
+        // The window is focusable by default
+        window.setFocusable(true)
+        viewProxy.requestFocus()
+
+        enqueueScrollCaptureRequest(requestId, screenshot.userHandle!!)
+
+        window.attachWindow()
+
+        val showFlash: Boolean
+        if (screenshot.type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+            if (aspectRatiosMatch(currentBitmap, screenshot.insets, screenshot.screenBounds)) {
+                showFlash = false
+            } else {
+                showFlash = true
+                screenshot.insets = Insets.NONE
+                screenshot.screenBounds = Rect(0, 0, currentBitmap.width, currentBitmap.height)
+            }
+        } else {
+            showFlash = true
+        }
+
+        // screenshot.screenBounds is expected to be non-null in all cases at this point
+        val bounds =
+            screenshot.screenBounds ?: Rect(0, 0, currentBitmap.width, currentBitmap.height)
+
+        viewProxy.prepareEntranceAnimation {
+            startAnimation(bounds, showFlash) {
+                messageContainerController.onScreenshotTaken(screenshot)
+            }
+        }
+
+        viewProxy.screenshot = screenshot
+    }
+
+    private fun prepareViewForNewScreenshot(screenshot: ScreenshotData, oldPackageName: String?) {
+        window.whenWindowAttached {
+            announcementResolver.getScreenshotAnnouncement(screenshot.userHandle!!.identifier) {
+                viewProxy.announceForAccessibility(it)
+            }
+        }
+
+        viewProxy.reset()
+
+        if (viewProxy.isAttachedToWindow) {
+            // if we didn't already dismiss for another reason
+            if (!viewProxy.isDismissing) {
+                uiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0, oldPackageName)
+            }
+            if (LogConfig.DEBUG_WINDOW) {
+                Log.d(
+                    TAG,
+                    "saveScreenshot: screenshotView is already attached, resetting. " +
+                        "(dismissing=${viewProxy.isDismissing})",
+                )
+            }
+        }
+
+        viewProxy.packageName = packageName
+    }
+
+    /**
+     * Requests the view to dismiss the current screenshot (may be ignored, if screenshot is already
+     * being dismissed)
+     */
+    override fun requestDismissal(event: ScreenshotEvent) {
+        viewProxy.requestDismissal(event)
+    }
+
+    override fun isPendingSharedTransition(): Boolean {
+        return actionExecutor.isPendingSharedTransition
+    }
+
+    // Any cleanup needed when the service is being destroyed.
+    override fun onDestroy() {
+        removeWindow()
+        releaseMediaPlayer()
+        releaseContext()
+        bgExecutor.shutdown()
+    }
+
+    /** Release the constructed window context. */
+    private fun releaseContext() {
+        broadcastDispatcher.unregisterReceiver(copyBroadcastReceiver)
+        context.release()
+    }
+
+    private fun releaseMediaPlayer() {
+        screenshotSoundController?.releaseScreenshotSoundAsync()
+    }
+
+    /** Update resources on configuration change. Reinflate for theme/color changes. */
+    private fun reloadAssets() {
+        if (LogConfig.DEBUG_UI) {
+            Log.d(TAG, "reloadAssets()")
+        }
+
+        messageContainerController.setView(viewProxy.view)
+        viewProxy.callbacks =
+            object : ScreenshotViewCallback {
+                override fun onUserInteraction() {
+                    if (LogConfig.DEBUG_INPUT) {
+                        Log.d(TAG, "onUserInteraction")
+                    }
+                    screenshotHandler.resetTimeout()
+                }
+
+                override fun onDismiss() {
+                    finishDismiss()
+                }
+
+                override fun onTouchOutside() {
+                    // TODO(159460485): Remove this when focus is handled properly in the system
+                    window.setFocusable(false)
+                }
+            }
+
+        if (LogConfig.DEBUG_WINDOW) {
+            Log.d(TAG, "setContentView: " + viewProxy.view)
+        }
+        window.setContentView(viewProxy.view)
+    }
+
+    private fun enqueueScrollCaptureRequest(requestId: UUID, owner: UserHandle) {
+        // Wait until this window is attached to request because it is
+        // the reference used to locate the target window (below).
+        window.whenWindowAttached {
+            requestScrollCapture(requestId, owner)
+            window.setActivityConfigCallback(
+                object : ActivityConfigCallback {
+                    override fun onConfigurationChanged(
+                        overrideConfig: Configuration,
+                        newDisplayId: Int,
+                    ) {
+                        if (configChanges.applyNewConfig(context.resources)) {
+                            // Hide the scroll chip until we know it's available in this
+                            // orientation
+                            actionsController.onScrollChipInvalidated()
+                            // Delay scroll capture eval a bit to allow the underlying activity
+                            // to set up in the new orientation.
+                            screenshotHandler.postDelayed(
+                                { requestScrollCapture(requestId, owner) },
+                                150,
+                            )
+                            viewProxy.updateInsets(window.getWindowInsets())
+                            // Screenshot animation calculations won't be valid anymore, so just end
+                            screenshotAnimation?.let { currentAnimation ->
+                                if (currentAnimation.isRunning) {
+                                    currentAnimation.end()
+                                }
+                            }
+                        }
+                    }
+                }
+            )
+        }
+    }
+
+    private fun requestScrollCapture(requestId: UUID, owner: UserHandle) {
+        scrollCaptureExecutor.requestScrollCapture(display.displayId, window.getWindowToken()) {
+            response: ScrollCaptureResponse ->
+            uiEventLogger.log(
+                ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
+                0,
+                response.packageName,
+            )
+            actionsController.onScrollChipReady(requestId) {
+                onScrollButtonClicked(owner, response)
+            }
+        }
+    }
+
+    private fun onScrollButtonClicked(owner: UserHandle, response: ScrollCaptureResponse) {
+        if (LogConfig.DEBUG_INPUT) {
+            Log.d(TAG, "scroll chip tapped")
+        }
+        uiEventLogger.log(
+            ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED,
+            0,
+            response.packageName,
+        )
+        val newScreenshot = imageCapture.captureDisplay(display.displayId, null)
+        if (newScreenshot == null) {
+            Log.e(TAG, "Failed to capture current screenshot for scroll transition!")
+            return
+        }
+        // delay starting scroll capture to make sure scrim is up before the app moves
+        viewProxy.prepareScrollingTransition(response, newScreenshot, screenshotTakenInPortrait) {
+            executeBatchScrollCapture(response, owner)
+        }
+    }
+
+    private fun executeBatchScrollCapture(response: ScrollCaptureResponse, owner: UserHandle) {
+        scrollCaptureExecutor.executeBatchScrollCapture(
+            response,
+            {
+                val intent = createLongScreenshotIntent(owner, context)
+                context.startActivity(intent)
+            },
+            { viewProxy.restoreNonScrollingUi() },
+            { transitionDestination: Rect, onTransitionEnd: Runnable, longScreenshot: LongScreenshot
+                ->
+                viewProxy.startLongScreenshotTransition(
+                    transitionDestination,
+                    onTransitionEnd,
+                    longScreenshot,
+                )
+            },
+        )
+    }
+
+    override fun removeWindow() {
+        window.removeWindow()
+        viewProxy.stopInputListening()
+    }
+
+    private fun playCameraSoundIfNeeded() {
+        // the controller is not-null only on the default display controller
+        screenshotSoundController?.playScreenshotSoundAsync()
+    }
+
+    /**
+     * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
+     * failure).
+     */
+    private fun saveScreenshotAndToast(screenshot: ScreenshotData, finisher: Consumer<Uri?>) {
+        // Play the shutter sound to notify that we've taken a screenshot
+        playCameraSoundIfNeeded()
+
+        saveScreenshotInBackground(screenshot, UUID.randomUUID(), finisher) {
+            result: ImageExporter.Result ->
+            if (result.uri != null) {
+                screenshotHandler.post {
+                    Toast.makeText(context, R.string.screenshot_saved_title, Toast.LENGTH_SHORT)
+                        .show()
+                }
+            }
+        }
+    }
+
+    /** Starts the animation after taking the screenshot */
+    private fun startAnimation(
+        screenRect: Rect,
+        showFlash: Boolean,
+        onAnimationComplete: Runnable?,
+    ) {
+        screenshotAnimation?.let { currentAnimation ->
+            if (currentAnimation.isRunning) {
+                currentAnimation.cancel()
+            }
+        }
+
+        screenshotAnimation =
+            viewProxy.createScreenshotDropInAnimation(screenRect, showFlash).apply {
+                doOnEnd { onAnimationComplete?.run() }
+                // Play the shutter sound to notify that we've taken a screenshot
+                playCameraSoundIfNeeded()
+                if (LogConfig.DEBUG_ANIM) {
+                    Log.d(TAG, "starting post-screenshot animation")
+                }
+                start()
+            }
+    }
+
+    /** Reset screenshot view and then call onCompleteRunnable */
+    private fun finishDismiss() {
+        Log.d(TAG, "finishDismiss")
+        actionsController.endScreenshotSession()
+        scrollCaptureExecutor.close()
+        currentRequestCallback?.onFinish()
+        currentRequestCallback = null
+        viewProxy.reset()
+        removeWindow()
+        screenshotHandler.cancelTimeout()
+    }
+
+    private fun saveScreenshotInBackground(
+        screenshot: ScreenshotData,
+        requestId: UUID,
+        finisher: Consumer<Uri?>,
+        onResult: Consumer<ImageExporter.Result>,
+    ) {
+        val future =
+            imageExporter.export(
+                bgExecutor,
+                requestId,
+                screenshot.bitmap,
+                screenshot.getUserOrDefault(),
+                display.displayId,
+            )
+        future.addListener(
+            {
+                try {
+                    val result = future.get()
+                    Log.d(TAG, "Saved screenshot: $result")
+                    logScreenshotResultStatus(result.uri, screenshot.userHandle!!)
+                    onResult.accept(result)
+                    if (LogConfig.DEBUG_CALLBACK) {
+                        Log.d(TAG, "finished bg processing, calling back with uri: ${result.uri}")
+                    }
+                    finisher.accept(result.uri)
+                } catch (e: Exception) {
+                    Log.d(TAG, "Failed to store screenshot", e)
+                    if (LogConfig.DEBUG_CALLBACK) {
+                        Log.d(TAG, "calling back with uri: null")
+                    }
+                    finisher.accept(null)
+                }
+            },
+            mainExecutor,
+        )
+    }
+
+    /** Logs success/failure of the screenshot saving task, and shows an error if it failed. */
+    private fun logScreenshotResultStatus(uri: Uri?, owner: UserHandle) {
+        if (uri == null) {
+            uiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, packageName)
+            notificationController.notifyScreenshotError(R.string.screenshot_failed_to_save_text)
+        } else {
+            uiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, packageName)
+            if (userManager.isManagedProfile(owner.identifier)) {
+                uiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 0, packageName)
+            }
+        }
+    }
+
+    private fun isUserSetupComplete(owner: UserHandle): Boolean {
+        return Settings.Secure.getInt(
+            context.createContextAsUser(owner, 0).contentResolver,
+            SETTINGS_SECURE_USER_SETUP_COMPLETE,
+            0,
+        ) == 1
+    }
+
+    private val fullScreenRect: Rect
+        get() {
+            val displayMetrics = DisplayMetrics()
+            display.getRealMetrics(displayMetrics)
+            return Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)
+        }
+
+    /** Injectable factory to create screenshot controller instances for a specific display. */
+    @AssistedFactory
+    interface Factory : InteractiveScreenshotHandler.Factory {
+        /**
+         * Creates an instance of the controller for that specific display.
+         *
+         * @param display display to capture
+         */
+        override fun create(display: Display): ScreenshotController
+    }
+
+    companion object {
+        private val TAG: String = LogConfig.logTag(ScreenshotController::class.java)
+
+        // From WizardManagerHelper.java
+        private const val SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete"
+
+        const val SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS: Int = 6000
+
+        /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
+        private fun aspectRatiosMatch(
+            bitmap: Bitmap,
+            bitmapInsets: Insets,
+            screenBounds: Rect?,
+        ): Boolean {
+            if (screenBounds == null) {
+                return false
+            }
+            val insettedWidth = bitmap.width - bitmapInsets.left - bitmapInsets.right
+            val insettedHeight = bitmap.height - bitmapInsets.top - bitmapInsets.bottom
+
+            if (
+                insettedHeight == 0 || insettedWidth == 0 || bitmap.width == 0 || bitmap.height == 0
+            ) {
+                if (LogConfig.DEBUG_UI) {
+                    Log.e(
+                        TAG,
+                        "Provided bitmap and insets create degenerate region: " +
+                            "${bitmap.width} x ${bitmap.height} $bitmapInsets",
+                    )
+                }
+                return false
+            }
+
+            val insettedBitmapAspect = insettedWidth.toFloat() / insettedHeight
+            val boundsAspect = screenBounds.width().toFloat() / screenBounds.height()
+
+            val matchWithinTolerance = abs((insettedBitmapAspect - boundsAspect).toDouble()) < 0.1f
+            if (LogConfig.DEBUG_UI) {
+                Log.d(
+                    TAG,
+                    "aspectRatiosMatch: don't match bitmap: " +
+                        "$insettedBitmapAspect, bounds: $boundsAspect",
+                )
+            }
+            return matchWithinTolerance
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 50215af..c1ea3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -67,7 +67,7 @@
     shelfViewBinder: ScreenshotShelfViewBinder,
     private val thumbnailObserver: ThumbnailObserver,
     @Assisted private val context: Context,
-    @Assisted private val displayId: Int
+    @Assisted private val displayId: Int,
 ) {
 
     interface ScreenshotViewCallback {
@@ -117,7 +117,7 @@
             animationController,
             LayoutInflater.from(context),
             onDismissalRequested = { event, velocity -> requestDismissal(event, velocity) },
-            onUserInteraction = { callbacks?.onUserInteraction() }
+            onUserInteraction = { callbacks?.onUserInteraction() },
         )
         view.updateInsets(windowManager.currentWindowMetrics.windowInsets)
         addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) }
@@ -130,7 +130,7 @@
         screenshotPreview = view.screenshotPreview
         thumbnailObserver.setViews(
             view.blurredScreenshotPreview,
-            view.requireViewById(R.id.screenshot_preview_border)
+            view.requireViewById(R.id.screenshot_preview_border),
         )
         view.addOnAttachStateChangeListener(
             object : View.OnAttachStateChangeListener {
@@ -204,7 +204,6 @@
 
     fun prepareScrollingTransition(
         response: ScrollCaptureResponse,
-        screenBitmap: Bitmap, // unused
         newScreenshot: Bitmap,
         screenshotTakenInPortrait: Boolean,
         onTransitionPrepared: Runnable,
@@ -224,7 +223,7 @@
                 0,
                 0,
                 context.resources.displayMetrics.widthPixels,
-                context.resources.displayMetrics.heightPixels
+                context.resources.displayMetrics.heightPixels,
             )
         )
         return r
@@ -239,7 +238,7 @@
             animationController.runLongScreenshotTransition(
                 transitionDestination,
                 longScreenshot,
-                onTransitionEnd
+                onTransitionEnd,
             )
         transitionAnimation.doOnEnd { callbacks?.onDismiss() }
         transitionAnimation.start()
@@ -295,7 +294,7 @@
                         .findOnBackInvokedDispatcher()
                         ?.registerOnBackInvokedCallback(
                             OnBackInvokedDispatcher.PRIORITY_DEFAULT,
-                            onBackInvokedCallback
+                            onBackInvokedCallback,
                         )
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 8e53949..649f8db 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -50,8 +50,12 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.systemui.Flags;
+import com.android.systemui.brightness.shared.model.BrightnessLog;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.LogLevel;
+import com.android.systemui.log.core.LogMessage;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.settings.SecureSettings;
@@ -60,6 +64,8 @@
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
 
+import kotlin.Unit;
+
 import java.util.concurrent.Executor;
 
 public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
@@ -88,6 +94,7 @@
     private final Executor mMainExecutor;
     private final Handler mBackgroundHandler;
     private final BrightnessObserver mBrightnessObserver;
+    private final LogBuffer mLogBuffer;
 
     private final DisplayTracker.Callback mBrightnessListener = new DisplayTracker.Callback() {
         @Override
@@ -308,6 +315,7 @@
             DisplayTracker displayTracker,
             DisplayManager displayManager,
             SecureSettings secureSettings,
+            @BrightnessLog LogBuffer logBuffer,
             @Nullable IVrManager iVrManager,
             @Main Executor mainExecutor,
             @Main Looper mainLooper,
@@ -323,6 +331,7 @@
         mDisplayId = mContext.getDisplayId();
         mDisplayManager = displayManager;
         mVrManager = iVrManager;
+        mLogBuffer = logBuffer;
 
         mMainHandler = new Handler(mainLooper, mHandlerCallback);
         mBrightnessObserver = new BrightnessObserver(mMainHandler);
@@ -342,6 +351,7 @@
 
     @Override
     public void onChanged(boolean tracking, int value, boolean stopTracking) {
+        boolean starting = !mTrackingTouch && tracking;
         mTrackingTouch = tracking;
         if (mExternalChange) return;
 
@@ -369,9 +379,13 @@
 
         }
         setBrightness(valFloat);
+        if (starting) {
+            logBrightnessChange(mDisplayId, valFloat, true);
+        }
         if (!tracking) {
             AsyncTask.execute(new Runnable() {
                     public void run() {
+                        logBrightnessChange(mDisplayId, valFloat, false);
                         mDisplayManager.setBrightness(mDisplayId, valFloat);
                     }
                 });
@@ -474,4 +488,20 @@
         /** Create a {@link BrightnessController} */
         BrightnessController create(ToggleSlider toggleSlider);
     }
+
+    private void logBrightnessChange(int display, float value, boolean starting) {
+        mLogBuffer.log(
+                TAG,
+                LogLevel.DEBUG,
+                (LogMessage message) -> {
+                    message.setInt1(display);
+                    message.setDouble1(value);
+                    message.setBool1(starting);
+                    return Unit.INSTANCE;
+                },
+                (LogMessage message) -> "%s brightness set in display %d to %.3f".formatted(
+                        message.getBool1() ? "Starting" : "Finishing", message.getInt1(),
+                        message.getDouble1())
+        );
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 7fa9926..52cb8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -108,7 +108,7 @@
 
         fun dispatchTouchEvent(
             ev: MotionEvent?,
-            disallowInterceptConsumer: Consumer<Boolean>?
+            disallowInterceptConsumer: Consumer<Boolean>?,
         ): Boolean {
             disallowInterceptConsumer?.apply { consumers.add(this) }
 
@@ -252,9 +252,7 @@
      *
      * @throws RuntimeException if the view is already initialized
      */
-    fun initView(
-        context: Context,
-    ): View {
+    fun initView(context: Context): View {
         return initView(
             ComposeView(context).apply {
                 repeatWhenAttached {
@@ -291,6 +289,13 @@
         )
     }
 
+    private fun resetTouchMonitor() {
+        touchMonitor?.apply {
+            destroy()
+            touchMonitor = null
+        }
+    }
+
     /** Override for testing. */
     @VisibleForTesting
     internal fun initView(containerView: View): View {
@@ -299,51 +304,38 @@
             throw RuntimeException("Communal view has already been initialized")
         }
 
-        if (touchMonitor == null) {
-            touchMonitor =
-                ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
-                    init()
-                }
-        }
+        resetTouchMonitor()
+
+        touchMonitor =
+            ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
+                init()
+            }
+
         lifecycleRegistry.addObserver(touchLifecycleLogger)
         lifecycleRegistry.currentState = Lifecycle.State.CREATED
 
         communalContainerView = containerView
 
-        val topEdgeSwipeRegionWidth =
-            containerView.resources.getDimensionPixelSize(
-                R.dimen.communal_top_edge_swipe_region_height
-            )
-        val bottomEdgeSwipeRegionWidth =
-            containerView.resources.getDimensionPixelSize(
-                R.dimen.communal_bottom_edge_swipe_region_height
-            )
+        if (!Flags.hubmodeFullscreenVerticalSwipeFix()) {
+            val topEdgeSwipeRegionWidth =
+                containerView.resources.getDimensionPixelSize(
+                    R.dimen.communal_top_edge_swipe_region_height
+                )
+            val bottomEdgeSwipeRegionWidth =
+                containerView.resources.getDimensionPixelSize(
+                    R.dimen.communal_bottom_edge_swipe_region_height
+                )
 
-        // BouncerSwipeTouchHandler has a larger gesture area than we want, set an exclusion area so
-        // the gesture area doesn't overlap with widgets.
-        // TODO(b/323035776): adjust gesture area for portrait mode
-        containerView.repeatWhenAttached {
-            // Run when the touch handling lifecycle is RESUMED, meaning the hub is visible and not
-            // occluded.
-            lifecycleRegistry.repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                val ltr = containerView.layoutDirection == View.LAYOUT_DIRECTION_LTR
-
-                val backGestureInset =
-                    Rect(
-                        0,
-                        0,
-                        if (ltr) 0 else containerView.right,
-                        containerView.bottom,
-                    )
-
-                containerView.systemGestureExclusionRects =
-                    if (Flags.hubmodeFullscreenVerticalSwipeFix()) {
-                        listOf(
-                            // Disable back gestures on the left side of the screen, to avoid
-                            // conflicting with scene transitions.
-                            backGestureInset
-                        )
-                    } else {
+            // BouncerSwipeTouchHandler has a larger gesture area than we want, set an exclusion
+            // area so
+            // the gesture area doesn't overlap with widgets.
+            // TODO(b/323035776): adjust gesture area for portrait mode
+            containerView.repeatWhenAttached {
+                // Run when the touch handling lifecycle is RESUMED, meaning the hub is visible and
+                // not
+                // occluded.
+                lifecycleRegistry.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                    containerView.systemGestureExclusionRects =
                         listOf(
                             // Only allow swipe up to bouncer and swipe down to shade in the very
                             // top/bottom to avoid conflicting with widgets in the hub grid.
@@ -351,15 +343,13 @@
                                 0,
                                 topEdgeSwipeRegionWidth,
                                 containerView.right,
-                                containerView.bottom - bottomEdgeSwipeRegionWidth
-                            ),
-                            // Disable back gestures on the left side of the screen, to avoid
-                            // conflicting with scene transitions.
-                            backGestureInset
+                                containerView.bottom - bottomEdgeSwipeRegionWidth,
+                            )
                         )
+
+                    logger.d({ "Insets updated: $str1" }) {
+                        str1 = containerView.systemGestureExclusionRects.toString()
                     }
-                logger.d({ "Insets updated: $str1" }) {
-                    str1 = containerView.systemGestureExclusionRects.toString()
                 }
             }
         }
@@ -372,7 +362,7 @@
             containerView,
             anyOf(
                 keyguardInteractor.primaryBouncerShowing,
-                keyguardInteractor.alternateBouncerShowing
+                keyguardInteractor.alternateBouncerShowing,
             ),
             {
                 anyBouncerShowing = it
@@ -380,12 +370,12 @@
                     logger.d({ "New value for anyBouncerShowing: $bool1" }) { bool1 = it }
                 }
                 updateTouchHandlingState()
-            }
+            },
         )
         collectFlow(
             containerView,
             keyguardTransitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
-            { onLockscreen = it }
+            { onLockscreen = it },
         )
         collectFlow(
             containerView,
@@ -393,7 +383,7 @@
             {
                 hubShowing = it
                 updateTouchHandlingState()
-            }
+            },
         )
         collectFlow(
             containerView,
@@ -404,12 +394,12 @@
                 communalInteractor.editActivityShowing,
                 keyguardTransitionInteractor.isInTransition(
                     Edge.create(KeyguardState.GONE, KeyguardState.GLANCEABLE_HUB)
-                )
+                ),
             ),
             {
                 inEditModeTransition = it
                 updateTouchHandlingState()
-            }
+            },
         )
         collectFlow(
             containerView,
@@ -417,7 +407,7 @@
                 shadeInteractor.isAnyFullyExpanded,
                 shadeInteractor.isUserInteracting,
                 shadeInteractor.isShadeFullyCollapsed,
-                ::Triple
+                ::Triple,
             ),
             { (isFullyExpanded, isUserInteracting, isShadeFullyCollapsed) ->
                 shadeConsumingTouches = isUserInteracting
@@ -441,7 +431,7 @@
                         }
                     }
                 updateTouchHandlingState()
-            }
+            },
         )
         collectFlow(containerView, keyguardInteractor.isDreaming, { isDreaming = it })
 
@@ -493,6 +483,8 @@
 
         lifecycleRegistry.removeObserver(touchLifecycleLogger)
 
+        resetTouchMonitor()
+
         logger.d("Hub container disposed")
     }
 
@@ -628,7 +620,7 @@
             powerManager.userActivity(
                 SystemClock.uptimeMillis(),
                 PowerManager.USER_ACTIVITY_EVENT_TOUCH,
-                0
+                0,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index f76c5fd..0c05dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2220,6 +2220,7 @@
     @VisibleForTesting
     void onFlingEnd(boolean cancelled) {
         mIsFlinging = false;
+        mExpectingSynthesizedDown = false;
         // No overshoot when the animation ends
         setOverExpansionInternal(0, false /* isFromGesture */);
         setAnimator(null);
@@ -2352,7 +2353,6 @@
             return;
         }
         if (mExpectingSynthesizedDown) {
-            mExpectingSynthesizedDown = false;
             // Window never will receive touch events that typically trigger haptic on open.
             maybeVibrateOnOpening(false /* openingWithTouch */);
             fling(velocity > 1f ? 1000f * velocity : 0  /* expand */);
@@ -3994,6 +3994,9 @@
             }
             mExpandedFraction = Math.min(1f,
                     maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
+            if (mExpandedFraction > 0f && mExpectingSynthesizedDown) {
+                mExpectingSynthesizedDown = false;
+            }
             mShadeRepository.setLegacyShadeExpansion(mExpandedFraction);
             mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
             mExpansionDragDownAmountPx = h;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 1c223db..5473af3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -30,6 +30,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.core.view.ViewKt;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.keyguard.KeyguardUnfoldTransition;
@@ -38,6 +40,7 @@
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags;
 import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.SysUISingleton;
@@ -51,6 +54,7 @@
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.model.Edge;
+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.res.R;
@@ -111,6 +115,7 @@
     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
     private final QuickSettingsController mQuickSettingsController;
+    private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final GlanceableHubContainerController
             mGlanceableHubContainerController;
     private GestureDetector mPulsingWakeupGestureHandler;
@@ -140,6 +145,7 @@
     private final PanelExpansionInteractor mPanelExpansionInteractor;
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
 
+    private ViewGroup mBouncerParentView;
     /**
      * If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been
      * intercepted and all future touch events for the gesture should be processed by this view.
@@ -217,6 +223,7 @@
         mPulsingGestureListener = pulsingGestureListener;
         mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener;
         mNotificationInsetsController = notificationInsetsController;
+        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mGlanceableHubContainerController = glanceableHubContainerController;
         mFeatureFlagsClassic = featureFlagsClassic;
         mSysUIKeyEventHandler = sysUIKeyEventHandler;
@@ -227,7 +234,7 @@
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
         mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
-        bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
+        bindBouncer(bouncerViewBinder);
 
         collectFlow(mView, keyguardTransitionInteractor.transition(
                 Edge.create(LOCKSCREEN, DREAMING)),
@@ -256,6 +263,36 @@
         dumpManager.registerDumpable(this);
     }
 
+    private void bindBouncer(BouncerViewBinder bouncerViewBinder) {
+        mBouncerParentView = mView.findViewById(R.id.keyguard_bouncer_container);
+        bouncerViewBinder.bind(mBouncerParentView);
+        if (ComposeBouncerFlags.INSTANCE.isOnlyComposeBouncerEnabled()) {
+            collectFlow(mView, mKeyguardTransitionInteractor.transition(
+                            new Edge.StateToState(KeyguardState.PRIMARY_BOUNCER, null)),
+                    this::onTransitionAwayFromBouncer);
+            collectFlow(mView, mKeyguardTransitionInteractor.transition(
+                            new Edge.StateToState(null, KeyguardState.PRIMARY_BOUNCER)),
+                    this::onTransitionToBouncer);
+            collectFlow(mView, mPrimaryBouncerInteractor.isShowing(),
+                    (showing) -> ViewKt.setVisible(mBouncerParentView, showing));
+        }
+    }
+
+    private void onTransitionToBouncer(TransitionStep transitionStep) {
+        if (transitionStep.getTransitionState() == TransitionState.STARTED) {
+            if (mView.indexOfChild(mBouncerParentView) != -1) {
+                mView.removeView(mBouncerParentView);
+            }
+            mView.addView(mBouncerParentView);
+        }
+    }
+
+    private void onTransitionAwayFromBouncer(TransitionStep transitionStep) {
+        if (transitionStep.getTransitionState() == TransitionState.FINISHED) {
+            mView.removeView(mBouncerParentView);
+        }
+    }
+
     /**
      * @return Location where to place the KeyguardMessageArea
      */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 4ed4af6..830649b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -42,7 +42,6 @@
 import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
-import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
@@ -464,9 +463,6 @@
         mJavaAdapter.alwaysCollectFlow(
                 mCommunalTransitionViewModelLazy.get().isUmoOnCommunal(),
                 this::setShouldUpdateSquishinessOnMedia);
-        mJavaAdapter.alwaysCollectFlow(
-                mShadeInteractor.isAnyExpanded(),
-                this::onAnyExpandedChanged);
     }
 
     private void initNotificationStackScrollLayoutController() {
@@ -486,10 +482,6 @@
         }
     }
 
-    private void onAnyExpandedChanged(boolean isAnyExpanded) {
-        mQsFrame.setVisibility(isAnyExpanded ? View.VISIBLE : View.INVISIBLE);
-    }
-
     private void onNotificationScrolled(int newScrollPosition) {
         updateExpansionEnabledAmbient();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 361226a4..6c99282 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.Instant
 import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
 import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -80,18 +80,25 @@
         }
     }
 
+    @Deprecated("Deprecated in Java")
     override fun isShadeEnabled() = shadeInteractor.isShadeEnabled.value
 
+    @Deprecated("Deprecated in Java")
     override fun isShadeFullyOpen(): Boolean = shadeInteractor.isAnyFullyExpanded.value
 
+    @Deprecated("Deprecated in Java")
     override fun isExpandingOrCollapsing(): Boolean = shadeInteractor.isUserInteracting.value
 
+    @Deprecated("Deprecated in Java")
     override fun instantExpandShade() {
         // Do nothing
     }
 
     override fun instantCollapseShade() {
-        sceneInteractor.snapToScene(SceneFamilies.Home, "hide shade")
+        shadeInteractor.collapseNotificationsShade(
+            loggingReason = "ShadeControllerSceneImpl.instantCollapseShade",
+            transitionKey = Instant,
+        )
     }
 
     override fun animateCollapseShade(
@@ -122,16 +129,17 @@
         }
     }
 
+    @Deprecated("Deprecated in Java")
     override fun collapseWithDuration(animationDuration: Int) {
         // TODO(b/300258424) inline this. The only caller uses the default duration.
         animateCollapseShade()
     }
 
     private fun animateCollapseShadeInternal() {
-        sceneInteractor.changeScene(
-            SceneFamilies.Home, // TODO(b/336581871): add sceneState?
-            "ShadeController.animateCollapseShade",
-            SlightlyFasterShadeCollapse,
+        // TODO(b/336581871): add sceneState?
+        shadeInteractor.collapseEitherShade(
+            loggingReason = "ShadeController.animateCollapseShade",
+            transitionKey = SlightlyFasterShadeCollapse,
         )
     }
 
@@ -140,6 +148,7 @@
         animateCollapseShade()
     }
 
+    @Deprecated("Deprecated in Java")
     override fun closeShadeIfOpen(): Boolean {
         if (shadeInteractor.isAnyExpanded.value) {
             commandQueue.animateCollapsePanels(
@@ -155,6 +164,7 @@
         animateCollapseShadeForcedDelayed()
     }
 
+    @Deprecated("Deprecated in Java")
     override fun collapseShade(animate: Boolean) {
         if (animate) {
             animateCollapseShade()
@@ -163,13 +173,14 @@
         }
     }
 
+    @Deprecated("Deprecated in Java")
     override fun collapseOnMainThread() {
         // TODO if this works with delegation alone, we can deprecate and delete
         collapseShade()
     }
 
     override fun expandToNotifications() {
-        shadeInteractor.expandNotificationShade("ShadeController.animateExpandShade")
+        shadeInteractor.expandNotificationsShade("ShadeController.animateExpandShade")
     }
 
     override fun expandToQs() {
@@ -193,14 +204,17 @@
         }
     }
 
+    @Deprecated("Deprecated in Java")
     override fun postAnimateCollapseShade() {
         animateCollapseShade()
     }
 
+    @Deprecated("Deprecated in Java")
     override fun postAnimateForceCollapseShade() {
         animateCollapseShadeForced()
     }
 
+    @Deprecated("Deprecated in Java")
     override fun postAnimateExpandQs() {
         expandToQs()
     }
@@ -214,18 +228,23 @@
         }
     }
 
+    @Deprecated("Deprecated in Java")
     override fun makeExpandedInvisible() {
         // Do nothing
     }
 
+    @Deprecated("Deprecated in Java")
     override fun makeExpandedVisible(force: Boolean) {
         // Do nothing
     }
 
+    @Deprecated("Deprecated in Java")
     override fun isExpandedVisible(): Boolean {
-        return sceneInteractor.currentScene.value != Scenes.Gone
+        return sceneInteractor.currentScene.value != Scenes.Gone ||
+            sceneInteractor.currentOverlays.value.isNotEmpty()
     }
 
+    @Deprecated("Deprecated in Java")
     override fun onStatusBarTouch(event: MotionEvent) {
         // The only call to this doesn't happen with MigrateClocksToBlueprint.isEnabled enabled
         throw UnsupportedOperationException()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index b046c50..a3f2c64 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade.domain.interactor
 
 import androidx.annotation.FloatRange
+import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.shade.shared.model.ShadeMode
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -27,19 +28,22 @@
 
 /** Business logic for shade interactions. */
 interface ShadeInteractor : BaseShadeInteractor {
-    /** Emits true if the shade is currently allowed and false otherwise. */
+    /** Emits true if the Notifications shade is currently allowed and false otherwise. */
     val isShadeEnabled: StateFlow<Boolean>
 
-    /** Emits true if QS is currently allowed and false otherwise. */
+    /** Emits true if QS shade is currently allowed and false otherwise. */
     val isQsEnabled: StateFlow<Boolean>
 
-    /** Whether either the shade or QS is fully expanded. */
+    /** Whether either the Notifications shade or QS shade is fully expanded. */
     val isAnyFullyExpanded: StateFlow<Boolean>
 
-    /** Whether the Shade is fully expanded. */
+    /** Whether the Notifications Shade is fully expanded. */
     val isShadeFullyExpanded: Flow<Boolean>
 
-    /** Whether the Shade is fully collapsed. */
+    /** Whether Notifications Shade is expanded a non-zero amount. */
+    val isShadeAnyExpanded: StateFlow<Boolean>
+
+    /** Whether the Notifications Shade is fully collapsed. */
     val isShadeFullyCollapsed: Flow<Boolean>
 
     /**
@@ -102,7 +106,7 @@
      */
     val isAnyExpanded: StateFlow<Boolean>
 
-    /** The amount [0-1] that the shade has been opened. */
+    /** The amount [0-1] that the Notifications Shade has been opened. */
     val shadeExpansion: StateFlow<Float>
 
     /**
@@ -111,7 +115,7 @@
      */
     val qsExpansion: StateFlow<Float>
 
-    /** Whether Quick Settings is expanded a non-zero amount. */
+    /** Whether Quick Settings Shade is expanded a non-zero amount. */
     val isQsExpanded: StateFlow<Boolean>
 
     /**
@@ -142,16 +146,38 @@
     val isUserInteractingWithQs: Flow<Boolean>
 
     /**
-     * Triggers the expansion (opening) of the notification shade. If the notification shade is
-     * already open, this has no effect.
+     * Triggers the expansion (opening) of the notifications shade. If it is already expanded, this
+     * has no effect.
      */
-    fun expandNotificationShade(loggingReason: String)
+    fun expandNotificationsShade(loggingReason: String, transitionKey: TransitionKey? = null)
 
     /**
-     * Triggers the expansion (opening) of the quick settings shade. If the quick settings shade is
-     * already open, this has no effect.
+     * Triggers the expansion (opening) of the quick settings shade. If it is already expanded, this
+     * has no effect.
      */
-    fun expandQuickSettingsShade(loggingReason: String)
+    fun expandQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey? = null)
+
+    /**
+     * Triggers the collapse (closing) of the notifications shade. If it is already collapsed, this
+     * has no effect.
+     */
+    fun collapseNotificationsShade(loggingReason: String, transitionKey: TransitionKey? = null)
+
+    /**
+     * Triggers the collapse (closing) of the quick settings shade. If it is already collapsed, this
+     * has no effect.
+     */
+    fun collapseQuickSettingsShade(
+        loggingReason: String,
+        transitionKey: TransitionKey? = null,
+        bypassNotificationsShade: Boolean = false,
+    )
+
+    /**
+     * Triggers the collapse (closing) of the notifications shade or quick settings shade, whichever
+     * is open. If both are already collapsed, this has no effect.
+     */
+    fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey? = null)
 }
 
 fun createAnyExpansionFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
index fb14828..322fca3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shade.domain.interactor
 
+import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
@@ -31,6 +32,7 @@
     override val isShadeEnabled: StateFlow<Boolean> = inactiveFlowBoolean
     override val isQsEnabled: StateFlow<Boolean> = inactiveFlowBoolean
     override val shadeExpansion: StateFlow<Float> = inactiveFlowFloat
+    override val isShadeAnyExpanded: StateFlow<Boolean> = inactiveFlowBoolean
     override val qsExpansion: StateFlow<Float> = inactiveFlowFloat
     override val isQsExpanded: StateFlow<Boolean> = inactiveFlowBoolean
     override val isQsBypassingShade: Flow<Boolean> = inactiveFlowBoolean
@@ -50,7 +52,17 @@
 
     override fun getTopEdgeSplitFraction(): Float = 0.5f
 
-    override fun expandNotificationShade(loggingReason: String) {}
+    override fun expandNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {}
 
-    override fun expandQuickSettingsShade(loggingReason: String) {}
+    override fun expandQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) {}
+
+    override fun collapseNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {}
+
+    override fun collapseQuickSettingsShade(
+        loggingReason: String,
+        transitionKey: TransitionKey?,
+        bypassNotificationsShade: Boolean,
+    ) {}
+
+    override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index 3eab02a..949d2aa 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -78,12 +78,16 @@
     override val isShadeFullyExpanded: Flow<Boolean> =
         baseShadeInteractor.shadeExpansion.map { it >= 1f }.distinctUntilChanged()
 
+    override val isShadeAnyExpanded: StateFlow<Boolean> =
+        baseShadeInteractor.shadeExpansion
+            .map { it > 0 }
+            .stateIn(scope, SharingStarted.Eagerly, false)
+
     override val isShadeFullyCollapsed: Flow<Boolean> =
         baseShadeInteractor.shadeExpansion.map { it <= 0f }.distinctUntilChanged()
 
     override val isUserInteracting: StateFlow<Boolean> =
         combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
-            .distinctUntilChanged()
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isShadeTouchable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
index df09486..0902c39 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade.domain.interactor
 
 import com.android.app.tracing.FlowTracing.traceAsCounter
+import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -111,18 +112,40 @@
     override val isUserInteractingWithQs: Flow<Boolean> =
         userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion)
 
-    override fun expandNotificationShade(loggingReason: String) {
+    override fun expandNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {
         throw UnsupportedOperationException(
             "expandNotificationShade() is not supported in legacy shade"
         )
     }
 
-    override fun expandQuickSettingsShade(loggingReason: String) {
+    override fun expandQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) {
         throw UnsupportedOperationException(
             "expandQuickSettingsShade() is not supported in legacy shade"
         )
     }
 
+    override fun collapseNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {
+        throw UnsupportedOperationException(
+            "collapseNotificationShade() is not supported in legacy shade"
+        )
+    }
+
+    override fun collapseQuickSettingsShade(
+        loggingReason: String,
+        transitionKey: TransitionKey?,
+        bypassNotificationsShade: Boolean,
+    ) {
+        throw UnsupportedOperationException(
+            "collapseQuickSettingsShade() is not supported in legacy shade"
+        )
+    }
+
+    override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) {
+        throw UnsupportedOperationException(
+            "collapseEitherShade() is not supported in legacy shade"
+        )
+    }
+
     /**
      * Return a flow for whether a user is interacting with an expandable shade component using
      * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that
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 81bf712..7658108 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
@@ -21,12 +21,16 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.Instant
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
@@ -133,43 +137,120 @@
             }
         }
 
-    override fun expandNotificationShade(loggingReason: String) {
+    override fun expandNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {
         if (shadeModeInteractor.isDualShade) {
             if (Overlays.QuickSettingsShade in sceneInteractor.currentOverlays.value) {
                 sceneInteractor.replaceOverlay(
                     from = Overlays.QuickSettingsShade,
                     to = Overlays.NotificationsShade,
                     loggingReason = loggingReason,
+                    transitionKey = transitionKey,
                 )
             } else {
                 sceneInteractor.showOverlay(
                     overlay = Overlays.NotificationsShade,
                     loggingReason = loggingReason,
+                    transitionKey = transitionKey,
                 )
             }
         } else {
-            sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = loggingReason)
+            sceneInteractor.changeScene(
+                toScene = Scenes.Shade,
+                loggingReason = loggingReason,
+                transitionKey =
+                    transitionKey ?: ToSplitShade.takeIf { shadeModeInteractor.isSplitShade },
+            )
         }
     }
 
-    override fun expandQuickSettingsShade(loggingReason: String) {
+    override fun expandQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) {
         if (shadeModeInteractor.isDualShade) {
             if (Overlays.NotificationsShade in sceneInteractor.currentOverlays.value) {
                 sceneInteractor.replaceOverlay(
                     from = Overlays.NotificationsShade,
                     to = Overlays.QuickSettingsShade,
                     loggingReason = loggingReason,
+                    transitionKey = transitionKey,
                 )
             } else {
                 sceneInteractor.showOverlay(
                     overlay = Overlays.QuickSettingsShade,
                     loggingReason = loggingReason,
+                    transitionKey = transitionKey,
                 )
             }
         } else {
+            val isSplitShade = shadeModeInteractor.isSplitShade
             sceneInteractor.changeScene(
-                toScene = Scenes.QuickSettings,
+                toScene = if (isSplitShade) Scenes.Shade else Scenes.QuickSettings,
                 loggingReason = loggingReason,
+                transitionKey = transitionKey ?: ToSplitShade.takeIf { isSplitShade },
+            )
+        }
+    }
+
+    override fun collapseNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {
+        if (shadeModeInteractor.isDualShade) {
+            // TODO(b/356596436): Hide without animation if transitionKey is Instant.
+            sceneInteractor.hideOverlay(
+                overlay = Overlays.NotificationsShade,
+                loggingReason = loggingReason,
+                transitionKey = transitionKey,
+            )
+        } else if (transitionKey == Instant) {
+            // TODO(b/356596436): Define instant transition instead of snapToScene().
+            sceneInteractor.snapToScene(toScene = SceneFamilies.Home, loggingReason = loggingReason)
+        } else {
+            sceneInteractor.changeScene(
+                toScene = SceneFamilies.Home,
+                loggingReason = loggingReason,
+                transitionKey =
+                    transitionKey ?: ToSplitShade.takeIf { shadeModeInteractor.isSplitShade },
+            )
+        }
+    }
+
+    override fun collapseQuickSettingsShade(
+        loggingReason: String,
+        transitionKey: TransitionKey?,
+        bypassNotificationsShade: Boolean,
+    ) {
+        if (shadeModeInteractor.isDualShade) {
+            // TODO(b/356596436): Hide without animation if transitionKey is Instant.
+            sceneInteractor.hideOverlay(
+                overlay = Overlays.QuickSettingsShade,
+                loggingReason = loggingReason,
+                transitionKey = transitionKey,
+            )
+            return
+        }
+
+        val isSplitShade = shadeModeInteractor.isSplitShade
+        val targetScene =
+            if (bypassNotificationsShade || isSplitShade) SceneFamilies.Home else Scenes.Shade
+        if (transitionKey == Instant) {
+            // TODO(b/356596436): Define instant transition instead of snapToScene().
+            sceneInteractor.snapToScene(toScene = targetScene, loggingReason = loggingReason)
+        } else {
+            sceneInteractor.changeScene(
+                toScene = targetScene,
+                loggingReason = loggingReason,
+                transitionKey = transitionKey ?: ToSplitShade.takeIf { isSplitShade },
+            )
+        }
+    }
+
+    override fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey?) {
+        // Note: The notifications shade and QS shade may be both partially expanded simultaneously,
+        // so we don't use an 'else' clause here.
+        if (shadeExpansion.value > 0) {
+            collapseNotificationsShade(loggingReason = loggingReason, transitionKey = transitionKey)
+        }
+        if (isQsExpanded.value) {
+            collapseQuickSettingsShade(
+                loggingReason = loggingReason,
+                transitionKey = transitionKey,
+                bypassNotificationsShade = true,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index 0fb3790..ea76ac4b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -21,10 +21,9 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.shared.model.KeyguardState
 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.scene.shared.model.TransitionKeys.Instant
 import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -48,7 +47,7 @@
 
     @Deprecated("Use ShadeInteractor instead")
     override fun expandToNotifications() {
-        shadeInteractor.expandNotificationShade(
+        shadeInteractor.expandNotificationsShade(
             loggingReason = "ShadeLockscreenInteractorImpl.expandToNotifications"
         )
     }
@@ -71,17 +70,11 @@
     }
 
     override fun resetViews(animate: Boolean) {
-        val loggingReason = "ShadeLockscreenInteractorImpl.resetViews"
         // The existing comment to the only call to this claims it only calls it to collapse QS
-        if (shadeInteractor.shadeMode.value == ShadeMode.Dual) {
-            // TODO(b/356596436): Hide without animation if !animate.
-            sceneInteractor.hideOverlay(
-                overlay = Overlays.QuickSettingsShade,
-                loggingReason = loggingReason,
-            )
-        } else {
-            shadeInteractor.expandNotificationShade(loggingReason)
-        }
+        shadeInteractor.collapseQuickSettingsShade(
+            loggingReason = "ShadeLockscreenInteractorImpl.resetViews",
+            transitionKey = Instant.takeIf { !animate },
+        )
     }
 
     @Deprecated("Not supported by scenes")
@@ -93,7 +86,7 @@
         backgroundScope.launch {
             delay(delay)
             withContext(mainDispatcher) {
-                shadeInteractor.expandNotificationShade(
+                shadeInteractor.expandNotificationsShade(
                     "ShadeLockscreenInteractorImpl.transitionToExpandedShade"
                 )
             }
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 caa4513..c838c37 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
@@ -55,6 +55,10 @@
     val isDualShade: Boolean
         get() = shadeMode.value is ShadeMode.Dual
 
+    /** Convenience shortcut for querying whether the current [shadeMode] is [ShadeMode.Split]. */
+    val isSplitShade: Boolean
+        get() = shadeMode.value is ShadeMode.Split
+
     /**
      * The fraction between [0..1] (i.e., percentage) of screen width to consider the threshold
      * between "top-left" and "top-right" for the purposes of dual-shade invocation.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index a154e91..bd4ed5b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -29,9 +29,7 @@
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.privacy.PrivacyItem
 import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.TransitionKeys
+import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
 import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
 import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -55,9 +53,8 @@
 class ShadeHeaderViewModel
 @AssistedInject
 constructor(
-    private val context: Context,
+    context: Context,
     private val activityStarter: ActivityStarter,
-    private val sceneInteractor: SceneInteractor,
     private val shadeInteractor: ShadeInteractor,
     private val mobileIconsInteractor: MobileIconsInteractor,
     val mobileIconsViewModel: MobileIconsViewModel,
@@ -120,7 +117,7 @@
                         map = { intent, _ ->
                             intent.action == Intent.ACTION_TIMEZONE_CHANGED ||
                                 intent.action == Intent.ACTION_LOCALE_CHANGED
-                        }
+                        },
                     )
                     .onEach { invalidateFormats -> updateDateTexts(invalidateFormats) }
                     .launchIn(this)
@@ -152,10 +149,9 @@
 
     /** Notifies that the system icons container was clicked. */
     fun onSystemIconContainerClicked() {
-        sceneInteractor.changeScene(
-            SceneFamilies.Home,
-            "ShadeHeaderViewModel.onSystemIconContainerClicked",
-            TransitionKeys.SlightlyFasterShadeCollapse,
+        shadeInteractor.collapseEitherShade(
+            loggingReason = "ShadeHeaderViewModel.onSystemIconContainerClicked",
+            transitionKey = SlightlyFasterShadeCollapse,
         )
     }
 
@@ -163,7 +159,7 @@
     fun onShadeCarrierGroupClicked() {
         activityStarter.postStartActivityDismissingKeyguard(
             Intent(Settings.ACTION_WIRELESS_SETTINGS),
-            0
+            0,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
deleted file mode 100644
index 2338be2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.annotation.ColorInt;
-import android.annotation.DrawableRes;
-import android.annotation.StringRes;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
-import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-
-public class EmptyShadeView extends StackScrollerDecorView {
-
-    private TextView mEmptyText;
-    private TextView mEmptyFooterText;
-
-    private @StringRes int mText = R.string.empty_shade_text;
-
-    private @DrawableRes int mFooterIcon = R.drawable.ic_friction_lock_closed;
-    private @StringRes int mFooterText = R.string.unlock_to_see_notif_text;
-    private @Visibility int mFooterVisibility = View.GONE;
-    private int mSize;
-
-    public EmptyShadeView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mSize = getResources().getDimensionPixelSize(
-                R.dimen.notifications_unseen_footer_icon_size);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mSize = getResources().getDimensionPixelSize(
-                R.dimen.notifications_unseen_footer_icon_size);
-        mEmptyText.setText(mText);
-        mEmptyFooterText.setVisibility(mFooterVisibility);
-        setFooterText(mFooterText);
-        setFooterIcon(mFooterIcon);
-    }
-
-    @Override
-    protected View findContentView() {
-        return findViewById(R.id.no_notifications);
-    }
-
-    @Override
-    protected View findSecondaryView() {
-        return findViewById(R.id.no_notifications_footer);
-    }
-
-    public void setTextColors(@ColorInt int onSurface, @ColorInt int onSurfaceVariant) {
-        mEmptyText.setTextColor(onSurfaceVariant);
-        mEmptyFooterText.setTextColor(onSurface);
-        mEmptyFooterText.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
-    }
-
-    public void setText(@StringRes int text) {
-        mText = text;
-        mEmptyText.setText(mText);
-    }
-
-    public void setFooterVisibility(@Visibility int visibility) {
-        mFooterVisibility = visibility;
-        setSecondaryVisible(/* visible = */ visibility == View.VISIBLE,
-                /* animate = */false,
-                /* onAnimationEnded = */ null);
-    }
-
-    public void setFooterText(@StringRes int text) {
-        mFooterText = text;
-        if (text != 0) {
-            mEmptyFooterText.setText(mFooterText);
-        } else {
-            mEmptyFooterText.setText(null);
-        }
-    }
-
-    public void setFooterIcon(@DrawableRes int icon) {
-        mFooterIcon = icon;
-        Drawable drawable;
-        if (icon == 0) {
-            drawable = null;
-        } else {
-            drawable = getResources().getDrawable(icon);
-            drawable.setBounds(0, 0, mSize, mSize);
-        }
-        mEmptyFooterText.setCompoundDrawablesRelative(drawable, null, null, null);
-    }
-
-    @StringRes
-    public int getTextResource() {
-        return mText;
-    }
-
-    @StringRes
-    public int getFooterTextResource() {
-        return mFooterText;
-    }
-
-    @DrawableRes
-    public int getFooterIconResource() {
-        return mFooterIcon;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mEmptyText = (TextView) findContentView();
-        mEmptyFooterText = (TextView) findSecondaryView();
-        mEmptyFooterText.setCompoundDrawableTintList(mEmptyFooterText.getTextColors());
-    }
-
-    @Override
-    @NonNull
-    public ExpandableViewState createExpandableViewState() {
-        return new EmptyShadeViewState();
-    }
-
-    public class EmptyShadeViewState extends ExpandableViewState {
-        @Override
-        public void applyToView(View view) {
-            super.applyToView(view);
-            if (view instanceof EmptyShadeView) {
-                EmptyShadeView emptyShadeView = (EmptyShadeView) view;
-                boolean visible = this.clipTopAmount <= mEmptyText.getPaddingTop() * 0.6f;
-                emptyShadeView.setContentVisibleAnimated(visible && emptyShadeView.isVisible());
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 5eef8ea..769abaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -207,8 +207,8 @@
     protected boolean mPowerPluggedInWireless;
     protected boolean mPowerPluggedInDock;
     protected int mChargingSpeed;
+    protected boolean mPowerCharged;
 
-    private boolean mPowerCharged;
     /** Whether the battery defender is triggered. */
     private boolean mBatteryDefender;
     /** Whether the battery defender is triggered with the device plugged. */
@@ -1100,14 +1100,15 @@
             String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
             return mContext.getResources().getString(
                     R.string.keyguard_plugged_in_incompatible_charger, percentage);
-        } else if (mPowerCharged) {
-            return mContext.getResources().getString(R.string.keyguard_charged);
         }
 
         return computePowerChargingStringIndication();
     }
 
     protected String computePowerChargingStringIndication() {
+        if (mPowerCharged) {
+            return mContext.getResources().getString(R.string.keyguard_charged);
+        }
         final boolean hasChargingTime = mChargingTimeRemaining > 0;
         int chargingId;
         if (mPowerPluggedInWired) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 7f55512..8a6ec2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -50,8 +50,8 @@
 import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus;
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.data.model.SceneStack;
@@ -115,6 +115,7 @@
     private final UiEventLogger mUiEventLogger;
     private final Lazy<InteractionJankMonitor> mInteractionJankMonitorLazy;
     private final JavaAdapter mJavaAdapter;
+    private final Lazy<KeyguardInteractor> mKeyguardInteractorLazy;
     private final Lazy<KeyguardTransitionInteractor> mKeyguardTransitionInteractorLazy;
     private final Lazy<ShadeInteractor> mShadeInteractorLazy;
     private final Lazy<DeviceUnlockedInteractor> mDeviceUnlockedInteractorLazy;
@@ -185,6 +186,7 @@
             UiEventLogger uiEventLogger,
             Lazy<InteractionJankMonitor> interactionJankMonitorLazy,
             JavaAdapter javaAdapter,
+            Lazy<KeyguardInteractor> keyguardInteractor,
             Lazy<KeyguardTransitionInteractor> keyguardTransitionInteractor,
             Lazy<ShadeInteractor> shadeInteractorLazy,
             Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
@@ -195,6 +197,7 @@
         mUiEventLogger = uiEventLogger;
         mInteractionJankMonitorLazy = interactionJankMonitorLazy;
         mJavaAdapter = javaAdapter;
+        mKeyguardInteractorLazy = keyguardInteractor;
         mKeyguardTransitionInteractorLazy = keyguardTransitionInteractor;
         mShadeInteractorLazy = shadeInteractorLazy;
         mDeviceUnlockedInteractorLazy = deviceUnlockedInteractorLazy;
@@ -233,8 +236,8 @@
                     this::onStatusBarStateChanged);
 
             mJavaAdapter.alwaysCollectFlow(
-                    mKeyguardTransitionInteractorLazy.get().transitionValue(KeyguardState.AOD),
-                    this::onAodKeyguardStateTransitionValueChanged);
+                    mKeyguardInteractorLazy.get().getDozeAmount(),
+                    this::setDozeAmountInternal);
         }
     }
 
@@ -404,6 +407,7 @@
 
     @Override
     public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) {
+        SceneContainerFlag.assertInLegacyMode();
         if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
             if (animated && mDozeAmountTarget == dozeAmount) {
                 return;
@@ -439,6 +443,7 @@
     }
 
     private void startDozeAnimation() {
+        SceneContainerFlag.assertInLegacyMode();
         if (mDozeAmount == 0f || mDozeAmount == 1f) {
             mDozeInterpolator = mIsDozing
                     ? Interpolators.FAST_OUT_SLOW_IN
@@ -457,6 +462,7 @@
 
     @VisibleForTesting
     protected ObjectAnimator createDarkAnimator() {
+        SceneContainerFlag.assertInLegacyMode();
         ObjectAnimator darkAnimator = ObjectAnimator.ofFloat(
                 this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
         darkAnimator.setInterpolator(Interpolators.LINEAR);
@@ -710,14 +716,6 @@
         updateStateAndNotifyListeners(newState);
     }
 
-    private void onAodKeyguardStateTransitionValueChanged(float value) {
-        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
-            return;
-        }
-
-        setDozeAmountInternal(value);
-    }
-
     private static final Map<SceneKey, Integer> sStatusBarStateByLockedSceneKey = Map.of(
             Scenes.Lockscreen, StatusBarState.KEYGUARD,
             Scenes.Bouncer, StatusBarState.KEYGUARD,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConntectedDisplays.kt
similarity index 83%
copy from packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConntectedDisplays.kt
index 4c0c461..54a18f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarConntectedDisplays.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.shared
+package com.android.systemui.statusbar.core
 
 import com.android.systemui.Flags
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
-/** Helper for reading or using the status bar ron chips flag state. */
+/** Helper for reading or using the status bar connected displays flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object StatusBarRonChips {
+object StatusBarConnectedDisplays {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS
 
     /** A token used for dependency declaration */
     val token: FlagToken
@@ -33,7 +33,7 @@
     /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.statusBarRonChips()
+        get() = Flags.statusBarConnectedDisplays()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
@@ -45,7 +45,7 @@
         RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
 
     /**
-     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * 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!
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 975b92e..3ad76b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -16,6 +16,8 @@
 package com.android.systemui.statusbar.core
 
 import android.app.Fragment
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.res.R
@@ -72,11 +74,38 @@
     private val windowController: StatusBarWindowController,
     private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
     private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
-) : StatusBarInitializer {
+) : CoreStartable, StatusBarInitializer {
+    private var component: StatusBarFragmentComponent? = null
+
+    @get:VisibleForTesting
+    var initialized = false
+        private set
 
     override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
+        set(value) {
+            field = value
+            // If a listener is added after initialization, immediately call the callback
+            component?.let { component ->
+                field?.onStatusBarViewUpdated(
+                    component.phoneStatusBarViewController,
+                    component.phoneStatusBarTransitions,
+                )
+            }
+        }
+
+    override fun start() {
+        if (StatusBarSimpleFragment.isEnabled) {
+            doStart()
+        }
+    }
 
     override fun initializeStatusBar() {
+        StatusBarSimpleFragment.assertInLegacyMode()
+        doStart()
+    }
+
+    private fun doStart() {
+        initialized = true
         windowController.fragmentHostManager
             .addTagListener(
                 CollapsedStatusBarFragment.TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
index c8f0b52..525f3de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
@@ -16,17 +16,28 @@
 
 package com.android.systemui.statusbar.dagger;
 
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.emergency.EmergencyGestureModule;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
 import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 
+import dagger.Binds;
 import dagger.Module;
 
-/**  */
+/**
+ * Dagger Module providing {@link CentralSurfacesImpl}.
+ */
 @Module(includes = {CentralSurfacesDependenciesModule.class,
-        StatusBarNotificationPresenterModule.class, StatusBarPhoneModule.class,
+        StatusBarNotificationPresenterModule.class,
         NotificationsModule.class, NotificationRowModule.class, EmergencyGestureModule.class})
 public interface CentralSurfacesModule {
+    /**
+     * Provides our instance of CentralSurfaces which is considered optional.
+     */
+    @Binds
+    @SysUISingleton
+    CentralSurfaces bindsCentralSurfaces(CentralSurfacesImpl impl);
 }
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 55943a5..3903ff3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -22,8 +22,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.core.StatusBarInitializer
-import com.android.systemui.statusbar.core.StatusBarInitializerImpl
 import com.android.systemui.statusbar.data.StatusBarDataLayerModule
 import com.android.systemui.statusbar.phone.LightBarController
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
@@ -63,8 +61,6 @@
     @ClassKey(StatusBarSignalPolicy::class)
     abstract fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable
 
-    @Binds abstract fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer
-
     companion object {
 
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
index 2bb4765..ce25cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
@@ -17,8 +17,12 @@
 import android.app.StatusBarManager.DISABLE2_NONE
 import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
 import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
+import android.app.StatusBarManager.DISABLE_CLOCK
 import android.app.StatusBarManager.DISABLE_NONE
 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
+import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
@@ -27,12 +31,14 @@
  * Model for the disable flags that come from [IStatusBar].
  *
  * For clients of the disable flags: do *not* refer to the disable integers directly. Instead,
- * re-use or define a helper method that internally processes the flags. (We want to hide the
- * bitwise logic here so no one else has to worry about it.)
+ * re-use or define a helper method or property that internally processes the flags. (We want to
+ * hide the bitwise logic here so no one else has to worry about it.)
  */
 data class DisableFlagsModel(
     private val disable1: Int = DISABLE_NONE,
     private val disable2: Int = DISABLE2_NONE,
+    /** True if we should animate any view visibility changes and false otherwise. */
+    val animate: Boolean = false,
 ) {
     /** Returns true if notification alerts are allowed based on the flags. */
     fun areNotificationAlertsEnabled(): Boolean {
@@ -49,6 +55,13 @@
         return (disable2 and DISABLE2_QUICK_SETTINGS) == 0
     }
 
+    val isClockEnabled = (disable1 and DISABLE_CLOCK) == 0
+
+    val areNotificationIconsEnabled = (disable1 and DISABLE_NOTIFICATION_ICONS) == 0
+
+    val isSystemInfoEnabled =
+        (disable1 and DISABLE_SYSTEM_INFO) == 0 && (disable2 and DISABLE2_SYSTEM_ICONS) == 0
+
     /** Logs the change to the provided buffer. */
     fun logChange(buffer: LogBuffer, disableFlagsLogger: DisableFlagsLogger) {
         buffer.log(
@@ -60,9 +73,9 @@
             },
             {
                 disableFlagsLogger.getDisableFlagsString(
-                    new = DisableFlagsLogger.DisableState(int1, int2),
+                    new = DisableFlagsLogger.DisableState(int1, int2)
                 )
-            }
+            },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
index 13b74b4..9004e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
@@ -72,6 +72,7 @@
                                     // [QuickSettingsInteractor]-type class. However, that's out of
                                     // scope for the CentralSurfaces removal project.
                                     remoteInputQuickSettingsDisabler.adjustDisableFlags(state2),
+                                    animate,
                                 )
                             )
                         }
@@ -82,5 +83,5 @@
             .distinctUntilChanged()
             .onEach { it.logChange(logBuffer, disableFlagsLogger) }
             // Use Eagerly because we always need to know about disable flags
-            .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel())
+            .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel(animate = false))
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
index 02a29e2..1c840e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
@@ -105,7 +105,8 @@
     private final HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
     private final ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
             = new ArraySet<>();
-    private boolean mIsExpanded;
+    private boolean mIsShadeOrQsExpanded;
+    private boolean mIsQsExpanded;
     private int mStatusBarState;
     private AnimationStateHandler mAnimationStateHandler;
 
@@ -177,6 +178,10 @@
         });
         javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(),
                     this::onShadeOrQsExpanded);
+        if (SceneContainerFlag.isEnabled()) {
+            javaAdapter.alwaysCollectFlow(shadeInteractor.isQsExpanded(),
+                    this::onQsExpanded);
+        }
         if (NotificationThrottleHun.isEnabled()) {
             mVisualStabilityProvider.addPersistentReorderingBannedListener(
                     mOnReorderingBannedListener);
@@ -286,14 +291,19 @@
     }
 
     private void onShadeOrQsExpanded(Boolean isExpanded) {
-        if (isExpanded != mIsExpanded) {
-            mIsExpanded = isExpanded;
+        if (isExpanded != mIsShadeOrQsExpanded) {
+            mIsShadeOrQsExpanded = isExpanded;
             if (!SceneContainerFlag.isEnabled() && isExpanded) {
                 mHeadsUpAnimatingAway.setValue(false);
             }
         }
     }
 
+    private void onQsExpanded(Boolean isQsExpanded) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        if (isQsExpanded != mIsQsExpanded) mIsQsExpanded = isQsExpanded;
+    }
+
     /**
      * Set that we are exiting the headsUp pinned mode, but some notifications might still be
      * animating out. This is used to keep the touchable regions in a reasonable state.
@@ -489,7 +499,10 @@
 
     @Override
     protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
-        boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsExpanded;
+        boolean pin = mStatusBarState == StatusBarState.SHADE && !mIsShadeOrQsExpanded;
+        if (SceneContainerFlag.isEnabled()) {
+            pin |= mIsQsExpanded;
+        }
         if (mBypassController.getBypassEnabled()) {
             pin |= mStatusBarState == StatusBarState.KEYGUARD;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
deleted file mode 100644
index ec3c7d0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 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.content.Intent;
-import android.view.View;
-
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
-/**
- * Component responsible for handling actions on a notification which cause activites to start.
- * (e.g. clicking on a notification, tapping on the settings icon in the notification guts)
- */
-public interface NotificationActivityStarter {
-    /** Called when the user clicks on the notification bubble icon. */
-    void onNotificationBubbleIconClicked(NotificationEntry entry);
-
-    /** Called when the user clicks on the surface of a notification. */
-    void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row);
-
-    /** Called when the user clicks on a button in the notification guts which fires an intent. */
-    void startNotificationGutsIntent(Intent intent, int appUid,
-            ExpandableNotificationRow row);
-
-    /** Called when the user clicks "Manage" or "History" in the Shade. */
-    void startHistoryIntent(View view, boolean showHistory);
-
-    /** Called when the user succeed to drop notification to proper target view. */
-    void onDragSuccess(NotificationEntry entry);
-
-    default boolean isCollapsingToShowActivityOverLockscreen() {
-        return false;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt
new file mode 100644
index 0000000..231a0b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt
@@ -0,0 +1,103 @@
+/*
+ * 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
+
+import android.content.Intent
+import android.provider.Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS
+import android.provider.Settings.ACTION_NOTIFICATION_HISTORY
+import android.provider.Settings.ACTION_NOTIFICATION_SETTINGS
+import android.provider.Settings.ACTION_ZEN_MODE_SETTINGS
+import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID
+import android.view.View
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/**
+ * Component responsible for handling actions on a notification which cause activites to start.
+ * (e.g. clicking on a notification, tapping on the settings icon in the notification guts)
+ */
+interface NotificationActivityStarter {
+
+    /** Called when the user clicks on the notification bubble icon. */
+    fun onNotificationBubbleIconClicked(entry: NotificationEntry?)
+
+    /** Called when the user clicks on the surface of a notification. */
+    fun onNotificationClicked(entry: NotificationEntry?, row: ExpandableNotificationRow?)
+
+    /** Called when the user clicks on a button in the notification guts which fires an intent. */
+    fun startNotificationGutsIntent(intent: Intent?, appUid: Int, row: ExpandableNotificationRow?)
+
+    /**
+     * Called when the user clicks "Manage" or "History" in the Shade. Prefer using
+     * [startSettingsIntent] instead.
+     */
+    fun startHistoryIntent(view: View?, showHistory: Boolean)
+
+    /**
+     * Called to open a settings intent from a launchable view (such as the "Manage" or "History"
+     * button in the shade, or the "No notifications" text).
+     *
+     * @param view the view to perform the launch animation from (must extend [LaunchableView])
+     * @param intentInfo information about the (settings) intent to be launched
+     */
+    fun startSettingsIntent(view: View, intentInfo: SettingsIntent)
+
+    /** Called when the user succeed to drop notification to proper target view. */
+    fun onDragSuccess(entry: NotificationEntry?)
+
+    val isCollapsingToShowActivityOverLockscreen: Boolean
+        get() = false
+
+    /**
+     * Information about a settings intent to be launched.
+     *
+     * If the [targetIntent] is T and [backStack] is [A, B, C], the stack will look like
+     * [A, B, C, T].
+     */
+    data class SettingsIntent(
+        var targetIntent: Intent,
+        var backStack: List<Intent> = emptyList(),
+        var cujType: Int? = null,
+    ) {
+        // Utility factory methods for known intents
+        companion object {
+            fun forNotificationSettings(cujType: Int? = null) =
+                SettingsIntent(
+                    targetIntent = Intent(ACTION_NOTIFICATION_SETTINGS),
+                    cujType = cujType,
+                )
+
+            fun forNotificationHistory(cujType: Int? = null) =
+                SettingsIntent(
+                    targetIntent = Intent(ACTION_NOTIFICATION_HISTORY),
+                    backStack = listOf(Intent(ACTION_NOTIFICATION_SETTINGS)),
+                    cujType = cujType,
+                )
+
+            fun forModesSettings(cujType: Int? = null) =
+                SettingsIntent(targetIntent = Intent(ACTION_ZEN_MODE_SETTINGS), cujType = cujType)
+
+            fun forModeSettings(modeId: String, cujType: Int? = null) =
+                SettingsIntent(
+                    targetIntent =
+                        Intent(ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
+                            .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, modeId),
+                    backStack = listOf(Intent(ACTION_ZEN_MODE_SETTINGS)),
+                    cujType = cujType,
+                )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index b342722..b67092c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -22,7 +22,7 @@
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
@@ -54,7 +54,7 @@
     fun getNotificationBuckets(): IntArray {
         if (
             PriorityPeopleSection.isEnabled ||
-                NotificationMinimalismPrototype.isEnabled ||
+                NotificationMinimalism.isEnabled ||
                 NotificationClassificationFlag.isEnabled
         ) {
             // We don't need this list to be adaptive, it can be the superset of all features.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
index a621b2a..4e63b92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
@@ -35,7 +35,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_ONGOING
 import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_UNSEEN
 import com.android.systemui.util.asIndenting
@@ -77,7 +77,7 @@
     private var unseenFilterEnabled = false
 
     override fun attach(pipeline: NotifPipeline) {
-        if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) {
+        if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) {
             return
         }
         pipeline.addPromoter(unseenNotifPromoter)
@@ -129,26 +129,25 @@
             }
     }
 
-    private fun unseenFeatureEnabled(): Flow<Boolean> {
-        // TODO(b/330387368): create LOCK_SCREEN_NOTIFICATION_MINIMALISM setting to use here?
-        //  Or should we actually just repurpose using the existing setting?
-        if (NotificationMinimalismPrototype.isEnabled) {
-            return flowOf(true)
+    private fun minimalismFeatureSettingEnabled(): Flow<Boolean> {
+        if (!NotificationMinimalism.isEnabled) {
+            return flowOf(false)
         }
-        return seenNotificationsInteractor.isLockScreenShowOnlyUnseenNotificationsEnabled()
+        return seenNotificationsInteractor.isLockScreenNotificationMinimalismEnabled()
     }
 
     private suspend fun trackUnseenFilterSettingChanges() {
-        unseenFeatureEnabled().collectLatest { isSettingEnabled ->
+        // Only filter the seen notifs when the lock screen minimalism feature settings is on.
+        minimalismFeatureSettingEnabled().collectLatest { isMinimalismSettingEnabled ->
             // update local field and invalidate if necessary
-            if (isSettingEnabled != unseenFilterEnabled) {
-                unseenFilterEnabled = isSettingEnabled
+            if (isMinimalismSettingEnabled != unseenFilterEnabled) {
+                unseenFilterEnabled = isMinimalismSettingEnabled
                 unseenNotifications.clear()
                 unseenNotifPromoter.invalidateList("unseen setting changed")
             }
             // if the setting is enabled, then start tracking and filtering unseen notifications
-            logger.logTrackingUnseen(isSettingEnabled)
-            if (isSettingEnabled) {
+            logger.logTrackingUnseen(isMinimalismSettingEnabled)
+            if (isMinimalismSettingEnabled) {
                 trackSeenNotifications()
             }
         }
@@ -178,7 +177,7 @@
         }
 
     private fun pickOutTopUnseenNotifs(list: List<ListEntry>) {
-        if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) return
+        if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return
         if (!unseenFilterEnabled) return
         // Only ever elevate a top unseen notification on keyguard, not even locked shade
         if (statusBarStateController.state != StatusBarState.KEYGUARD) {
@@ -215,9 +214,9 @@
         object : NotifPromoter(TAG) {
             override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
                 when {
-                    NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode() -> false
+                    NotificationMinimalism.isUnexpectedlyInLegacyMode() -> false
                     seenNotificationsInteractor.isTopOngoingNotification(child) -> true
-                    !NotificationMinimalismPrototype.ungroupTopUnseen -> false
+                    !NotificationMinimalism.ungroupTopUnseen -> false
                     else -> seenNotificationsInteractor.isTopUnseenNotification(child)
                 }
         }
@@ -225,7 +224,7 @@
     val topOngoingSectioner =
         object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) {
             override fun isInSection(entry: ListEntry): Boolean {
-                if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) return false
+                if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
                 return entry.anyEntry { notificationEntry ->
                     seenNotificationsInteractor.isTopOngoingNotification(notificationEntry)
                 }
@@ -235,7 +234,7 @@
     val topUnseenSectioner =
         object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) {
             override fun isInSection(entry: ListEntry): Boolean {
-                if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) return false
+                if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
                 return entry.anyEntry { notificationEntry ->
                     seenNotificationsInteractor.isTopUnseenNotification(notificationEntry)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorLogger.kt
index e44a77c..a4fa729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorLogger.kt
@@ -34,7 +34,10 @@
             TAG,
             LogLevel.DEBUG,
             messageInitializer = { bool1 = trackingUnseen },
-            messagePrinter = { "${if (bool1) "Start" else "Stop"} tracking unseen notifications." },
+            messagePrinter = {
+                "${if (bool1) "Start" else "Stop"} " +
+                    "tracking unseen notifications because of settings change."
+            },
         )
 
     fun logShadeVisible(numUnseen: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
index 0bbde21..82ce31b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -25,7 +25,6 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Flags;
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -61,8 +60,27 @@
                 return false;
             }
 
-            if (!Flags.notificationsBackgroundIcons()) {
-                inflateOrUpdateIcons(entry);
+            switch (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED)) {
+                case STATE_ICONS_UNINFLATED:
+                    try {
+                        mIconManager.createIcons(entry);
+                        mIconsState.put(entry, STATE_ICONS_INFLATED);
+                    } catch (InflationException e) {
+                        reportInflationError(entry, e);
+                        mIconsState.put(entry, STATE_ICONS_ERROR);
+                    }
+                    break;
+                case STATE_ICONS_INFLATED:
+                    try {
+                        mIconManager.updateIcons(entry, /* usingCache = */ false);
+                    } catch (InflationException e) {
+                        reportInflationError(entry, e);
+                        mIconsState.put(entry, STATE_ICONS_ERROR);
+                    }
+                    break;
+                case STATE_ICONS_ERROR:
+                    // do nothing
+                    break;
             }
 
             return true;
@@ -72,19 +90,7 @@
     private final NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
         @Override
         public void onEntryInit(@NonNull NotificationEntry entry) {
-            // We default to STATE_ICONS_UNINFLATED anyway, so there's no need to initialize it.
-            if (!Flags.notificationsBackgroundIcons()) {
-                mIconsState.put(entry, STATE_ICONS_UNINFLATED);
-            }
-        }
-
-        @Override
-        public void onEntryAdded(@NonNull NotificationEntry entry) {
-            if (Flags.notificationsBackgroundIcons()) {
-                if (isMediaNotification(entry.getSbn())) {
-                    inflateOrUpdateIcons(entry);
-                }
-            }
+            mIconsState.put(entry, STATE_ICONS_UNINFLATED);
         }
 
         @Override
@@ -93,12 +99,6 @@
                 // The update may have fixed the inflation error, so give it another chance.
                 mIconsState.put(entry, STATE_ICONS_UNINFLATED);
             }
-
-            if (Flags.notificationsBackgroundIcons()) {
-                if (isMediaNotification(entry.getSbn())) {
-                    inflateOrUpdateIcons(entry);
-                }
-            }
         }
 
         @Override
@@ -107,31 +107,6 @@
         }
     };
 
-    private void inflateOrUpdateIcons(NotificationEntry entry) {
-        switch (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED)) {
-            case STATE_ICONS_UNINFLATED:
-                try {
-                    mIconManager.createIcons(entry);
-                    mIconsState.put(entry, STATE_ICONS_INFLATED);
-                } catch (InflationException e) {
-                    reportInflationError(entry, e);
-                    mIconsState.put(entry, STATE_ICONS_ERROR);
-                }
-                break;
-            case STATE_ICONS_INFLATED:
-                try {
-                    mIconManager.updateIcons(entry, /* usingCache = */ false);
-                } catch (InflationException e) {
-                    reportInflationError(entry, e);
-                    mIconsState.put(entry, STATE_ICONS_ERROR);
-                }
-                break;
-            case STATE_ICONS_ERROR:
-                // do nothing
-                break;
-        }
-    }
-
     private void reportInflationError(NotificationEntry entry, Exception e) {
         // This is the same logic as in PreparationCoordinator; it doesn't handle media
         // notifications when the media feature is enabled since they aren't displayed in the shade,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 73ce48b..96c260b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
 import javax.inject.Inject
@@ -88,11 +88,10 @@
         mCoordinators.add(hideLocallyDismissedNotifsCoordinator)
         mCoordinators.add(hideNotifsForOtherUsersCoordinator)
         mCoordinators.add(keyguardCoordinator)
-        if (NotificationMinimalismPrototype.isEnabled) {
+        if (NotificationMinimalism.isEnabled) {
             mCoordinators.add(lockScreenMinimalismCoordinator)
-        } else {
-            mCoordinators.add(unseenKeyguardCoordinator)
         }
+        mCoordinators.add(unseenKeyguardCoordinator)
         mCoordinators.add(rankingCoordinator)
         mCoordinators.add(colorizedFgsCoordinator)
         mCoordinators.add(deviceProvisionedCoordinator)
@@ -125,11 +124,11 @@
         }
 
         // Manually add Ordered Sections
-        if (NotificationMinimalismPrototype.isEnabled) {
+        if (NotificationMinimalism.isEnabled) {
             mOrderedSections.add(lockScreenMinimalismCoordinator.topOngoingSectioner) // Top Ongoing
         }
         mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
-        if (NotificationMinimalismPrototype.isEnabled) {
+        if (NotificationMinimalism.isEnabled) {
             mOrderedSections.add(lockScreenMinimalismCoordinator.topUnseenSectioner) // Top Unseen
         }
         mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
index bfea2ba..cf1329c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
@@ -35,7 +35,6 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.asIndenting
@@ -51,7 +50,6 @@
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
@@ -87,7 +85,6 @@
     private var unseenFilterEnabled = false
 
     override fun attach(pipeline: NotifPipeline) {
-        NotificationMinimalismPrototype.assertInLegacyMode()
         pipeline.addFinalizeFilter(unseenNotifFilter)
         pipeline.addCollectionListener(collectionListener)
         scope.launch { trackUnseenFilterSettingChanges() }
@@ -253,10 +250,6 @@
     }
 
     private fun unseenFeatureEnabled(): Flow<Boolean> {
-        if (NotificationMinimalismPrototype.isEnabled) {
-            // TODO(b/330387368): should this really just be turned off? If so, hide the setting.
-            return flowOf(false)
-        }
         return seenNotificationsInteractor.isLockScreenShowOnlyUnseenNotificationsEnabled()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
index fe59d73..1fe32c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
@@ -45,17 +45,16 @@
 
 /**
  * How long to wait before auto-dismissing a notification that was kept for active remote input, and
- * has now sent a remote input. We auto-dismiss, because the app may not cannot cancel
- * these given that they technically don't exist anymore. We wait a bit in case the app issues
- * an update, and to also give the other lifetime extenders a beat to decide they want it.
+ * has now sent a remote input. We auto-dismiss, because the app may not cannot cancel these given
+ * that they technically don't exist anymore. We wait a bit in case the app issues an update, and to
+ * also give the other lifetime extenders a beat to decide they want it.
  */
 private const val REMOTE_INPUT_ACTIVE_EXTENDER_AUTO_CANCEL_DELAY: Long = 500
 
 /**
  * How long to wait before releasing a lifetime extension when requested to do so due to a user
- * interaction (such as tapping another action).
- * We wait a bit in case the app issues an update in response to the action, but not too long or we
- * risk appearing unresponsive to the user.
+ * interaction (such as tapping another action). We wait a bit in case the app issues an update in
+ * response to the action, but not too long or we risk appearing unresponsive to the user.
  */
 private const val REMOTE_INPUT_EXTENDER_RELEASE_DELAY: Long = 200
 
@@ -63,22 +62,21 @@
 private val DEBUG: Boolean by lazy { Log.isLoggable(TAG, Log.DEBUG) }
 
 @CoordinatorScope
-class RemoteInputCoordinator @Inject constructor(
+class RemoteInputCoordinator
+@Inject
+constructor(
     dumpManager: DumpManager,
     private val mRebuilder: RemoteInputNotificationRebuilder,
     private val mNotificationRemoteInputManager: NotificationRemoteInputManager,
     @Main private val mMainHandler: Handler,
-    private val mSmartReplyController: SmartReplyController
+    private val mSmartReplyController: SmartReplyController,
 ) : Coordinator, RemoteInputListener, Dumpable {
 
     @VisibleForTesting val mRemoteInputHistoryExtender = RemoteInputHistoryExtender()
     @VisibleForTesting val mSmartReplyHistoryExtender = SmartReplyHistoryExtender()
     @VisibleForTesting val mRemoteInputActiveExtender = RemoteInputActiveExtender()
-    private val mRemoteInputLifetimeExtenders = listOf(
-            mRemoteInputHistoryExtender,
-            mSmartReplyHistoryExtender,
-            mRemoteInputActiveExtender
-    )
+    private val mRemoteInputLifetimeExtenders =
+        listOf(mRemoteInputHistoryExtender, mSmartReplyHistoryExtender, mRemoteInputActiveExtender)
 
     private lateinit var mNotifUpdater: InternalNotifUpdater
 
@@ -93,9 +91,7 @@
         if (lifetimeExtensionRefactor()) {
             pipeline.addNotificationLifetimeExtender(mRemoteInputActiveExtender)
         } else {
-            mRemoteInputLifetimeExtenders.forEach {
-                pipeline.addNotificationLifetimeExtender(it)
-            }
+            mRemoteInputLifetimeExtenders.forEach { pipeline.addNotificationLifetimeExtender(it) }
         }
         mNotifUpdater = pipeline.getInternalNotifUpdater(TAG)
         pipeline.addCollectionListener(mCollectionListener)
@@ -105,65 +101,75 @@
      * Listener that updates the appearance of the notification if it has been lifetime extended
      * by a a direct reply or a smart reply, and cancelled.
      */
-    val mCollectionListener = object : NotifCollectionListener {
-        override fun onEntryUpdated(entry: NotificationEntry, fromSystem: Boolean) {
-            if (DEBUG) {
-                Log.d(TAG, "mCollectionListener.onEntryUpdated(entry=${entry.key}," +
-                        " fromSystem=$fromSystem)")
-            }
-            if (fromSystem) {
-                if (lifetimeExtensionRefactor()) {
-                    if ((entry.getSbn().getNotification().flags
-                                    and FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) {
-                        if (mNotificationRemoteInputManager.shouldKeepForRemoteInputHistory(
-                                        entry)) {
-                            val newSbn = mRebuilder.rebuildForRemoteInputReply(entry)
-                            entry.onRemoteInputInserted()
-                            mNotifUpdater.onInternalNotificationUpdate(newSbn,
-                                    "Extending lifetime of notification with remote input")
-                        } else if (mNotificationRemoteInputManager.shouldKeepForSmartReplyHistory(
-                                        entry)) {
-                            val newSbn = mRebuilder.rebuildForCanceledSmartReplies(entry)
-                            mSmartReplyController.stopSending(entry)
-                            mNotifUpdater.onInternalNotificationUpdate(newSbn,
-                                    "Extending lifetime of notification with smart reply")
+    val mCollectionListener =
+        object : NotifCollectionListener {
+            override fun onEntryUpdated(entry: NotificationEntry, fromSystem: Boolean) {
+                if (DEBUG) {
+                    Log.d(
+                        TAG,
+                        "mCollectionListener.onEntryUpdated(entry=${entry.key}," +
+                            " fromSystem=$fromSystem)",
+                    )
+                }
+                if (fromSystem) {
+                    if (lifetimeExtensionRefactor()) {
+                        if (
+                            (entry.getSbn().getNotification().flags and
+                                FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0
+                        ) {
+                            // If we've received an update from the system and the entry is marked
+                            // as lifetime extended, that means system server has received a
+                            // cancelation in response to a direct reply, and sent an update to
+                            // let system ui know that it should rebuild the notification with
+                            // that direct reply.
+                            if (
+                                mNotificationRemoteInputManager.shouldKeepForSmartReplyHistory(
+                                    entry
+                                )
+                            ) {
+                                val newSbn = mRebuilder.rebuildForCanceledSmartReplies(entry)
+                                mSmartReplyController.stopSending(entry)
+                                mNotifUpdater.onInternalNotificationUpdate(
+                                    newSbn,
+                                    "Extending lifetime of notification with smart reply",
+                                )
+                            } else {
+                                val newSbn = mRebuilder.rebuildForRemoteInputReply(entry)
+                                entry.onRemoteInputInserted()
+                                mNotifUpdater.onInternalNotificationUpdate(
+                                    newSbn,
+                                    "Extending lifetime of notification with remote input",
+                                )
+                            }
                         } else {
-                            // The app may have re-cancelled a notification after it had already
-                            // been lifetime extended.
-                            // Rebuild the notification with the replies it already had to ensure
-                            // those replies continue to be displayed.
-                            val newSbn = mRebuilder.rebuildWithExistingReplies(entry)
-                            mNotifUpdater.onInternalNotificationUpdate(newSbn,
-                                    "Extending lifetime of notification that has already been " +
-                                            "lifetime extended.")
+                            // Notifications updated without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY
+                            // should have their remote inputs list cleared.
+                            entry.remoteInputs = null
                         }
                     } else {
-                        // Notifications updated without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY
-                        // should have their remote inputs list cleared.
-                        entry.remoteInputs = null
+                        // Mark smart replies as sent whenever a notification is updated by the app,
+                        // otherwise the smart replies are never marked as sent.
+                        mSmartReplyController.stopSending(entry)
                     }
-                } else {
-                    // Mark smart replies as sent whenever a notification is updated by the app,
-                    // otherwise the smart replies are never marked as sent.
-                    mSmartReplyController.stopSending(entry)
+                }
+            }
+
+            override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+                if (DEBUG) Log.d(TAG, "mCollectionListener.onEntryRemoved(entry=${entry.key})")
+                // We're removing the notification, the smart reply controller can forget about it.
+                // TODO(b/145659174): track 'sending' state on the entry to avoid having to clear
+                // it.
+                mSmartReplyController.stopSending(entry)
+
+                // When we know the entry will not be lifetime extended, clean up the remote input
+                // view
+                // TODO: Share code with NotifCollection.cannotBeLifetimeExtended
+                if (reason == REASON_CANCEL || reason == REASON_CLICK) {
+                    mNotificationRemoteInputManager.cleanUpRemoteInputForUserRemoval(entry)
                 }
             }
         }
 
-        override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
-            if (DEBUG) Log.d(TAG, "mCollectionListener.onEntryRemoved(entry=${entry.key})")
-            // We're removing the notification, the smart reply controller can forget about it.
-            // TODO(b/145659174): track 'sending' state on the entry to avoid having to clear it.
-            mSmartReplyController.stopSending(entry)
-
-            // When we know the entry will not be lifetime extended, clean up the remote input view
-            // TODO: Share code with NotifCollection.cannotBeLifetimeExtended
-            if (reason == REASON_CANCEL || reason == REASON_CLICK) {
-                mNotificationRemoteInputManager.cleanUpRemoteInputForUserRemoval(entry)
-            }
-        }
-    }
-
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         mRemoteInputLifetimeExtenders.forEach { it.dump(pw, args) }
     }
@@ -183,22 +189,25 @@
         // view it is already canceled, so we'll need to cancel it on the apps behalf
         // now that a reply has been sent. However, delay so that the app has time to posts an
         // update in the mean time, and to give another lifetime extender time to pick it up.
-        mRemoteInputActiveExtender.endLifetimeExtensionAfterDelay(entry.key,
-                REMOTE_INPUT_ACTIVE_EXTENDER_AUTO_CANCEL_DELAY)
+        mRemoteInputActiveExtender.endLifetimeExtensionAfterDelay(
+            entry.key,
+            REMOTE_INPUT_ACTIVE_EXTENDER_AUTO_CANCEL_DELAY,
+        )
     }
 
     private fun onSmartReplySent(entry: NotificationEntry, reply: CharSequence) {
         if (DEBUG) Log.d(TAG, "onSmartReplySent(entry=${entry.key})")
         val newSbn = mRebuilder.rebuildForSendingSmartReply(entry, reply)
-        mNotifUpdater.onInternalNotificationUpdate(newSbn,
-                "Adding smart reply spinner for sent")
+        mNotifUpdater.onInternalNotificationUpdate(newSbn, "Adding smart reply spinner for sent")
 
         // If we're extending for remote input being active, then from the apps point of
         // view it is already canceled, so we'll need to cancel it on the apps behalf
         // now that a reply has been sent. However, delay so that the app has time to posts an
         // update in the mean time, and to give another lifetime extender time to pick it up.
-        mRemoteInputActiveExtender.endLifetimeExtensionAfterDelay(entry.key,
-                REMOTE_INPUT_ACTIVE_EXTENDER_AUTO_CANCEL_DELAY)
+        mRemoteInputActiveExtender.endLifetimeExtensionAfterDelay(
+            entry.key,
+            REMOTE_INPUT_ACTIVE_EXTENDER_AUTO_CANCEL_DELAY,
+        )
     }
 
     override fun onPanelCollapsed() {
@@ -208,19 +217,25 @@
     override fun isNotificationKeptForRemoteInputHistory(key: String) =
         if (!lifetimeExtensionRefactor()) {
             mRemoteInputHistoryExtender.isExtending(key) ||
-                    mSmartReplyHistoryExtender.isExtending(key)
+                mSmartReplyHistoryExtender.isExtending(key)
         } else false
 
     override fun releaseNotificationIfKeptForRemoteInputHistory(entry: NotificationEntry) {
         if (DEBUG) Log.d(TAG, "releaseNotificationIfKeptForRemoteInputHistory(entry=${entry.key})")
         if (!lifetimeExtensionRefactor()) {
-            mRemoteInputHistoryExtender.endLifetimeExtensionAfterDelay(entry.key,
-                    REMOTE_INPUT_EXTENDER_RELEASE_DELAY)
-            mSmartReplyHistoryExtender.endLifetimeExtensionAfterDelay(entry.key,
-                    REMOTE_INPUT_EXTENDER_RELEASE_DELAY)
+            mRemoteInputHistoryExtender.endLifetimeExtensionAfterDelay(
+                entry.key,
+                REMOTE_INPUT_EXTENDER_RELEASE_DELAY,
+            )
+            mSmartReplyHistoryExtender.endLifetimeExtensionAfterDelay(
+                entry.key,
+                REMOTE_INPUT_EXTENDER_RELEASE_DELAY,
+            )
         }
-        mRemoteInputActiveExtender.endLifetimeExtensionAfterDelay(entry.key,
-                REMOTE_INPUT_EXTENDER_RELEASE_DELAY)
+        mRemoteInputActiveExtender.endLifetimeExtensionAfterDelay(
+            entry.key,
+            REMOTE_INPUT_EXTENDER_RELEASE_DELAY,
+        )
     }
 
     override fun setRemoteInputController(remoteInputController: RemoteInputController) {
@@ -229,32 +244,36 @@
 
     @VisibleForTesting
     inner class RemoteInputHistoryExtender :
-            SelfTrackingLifetimeExtender(TAG, "RemoteInputHistory", DEBUG, mMainHandler) {
+        SelfTrackingLifetimeExtender(TAG, "RemoteInputHistory", DEBUG, mMainHandler) {
 
         override fun queryShouldExtendLifetime(entry: NotificationEntry): Boolean =
-                mNotificationRemoteInputManager.shouldKeepForRemoteInputHistory(entry)
+            mNotificationRemoteInputManager.shouldKeepForRemoteInputHistory(entry)
 
         override fun onStartedLifetimeExtension(entry: NotificationEntry) {
             val newSbn = mRebuilder.rebuildForRemoteInputReply(entry)
             entry.onRemoteInputInserted()
-            mNotifUpdater.onInternalNotificationUpdate(newSbn,
-                    "Extending lifetime of notification with remote input")
+            mNotifUpdater.onInternalNotificationUpdate(
+                newSbn,
+                "Extending lifetime of notification with remote input",
+            )
             // TODO: Check if the entry was removed due perhaps to an inflation exception?
         }
     }
 
     @VisibleForTesting
     inner class SmartReplyHistoryExtender :
-            SelfTrackingLifetimeExtender(TAG, "SmartReplyHistory", DEBUG, mMainHandler) {
+        SelfTrackingLifetimeExtender(TAG, "SmartReplyHistory", DEBUG, mMainHandler) {
 
         override fun queryShouldExtendLifetime(entry: NotificationEntry): Boolean =
-                mNotificationRemoteInputManager.shouldKeepForSmartReplyHistory(entry)
+            mNotificationRemoteInputManager.shouldKeepForSmartReplyHistory(entry)
 
         override fun onStartedLifetimeExtension(entry: NotificationEntry) {
             val newSbn = mRebuilder.rebuildForCanceledSmartReplies(entry)
             mSmartReplyController.stopSending(entry)
-            mNotifUpdater.onInternalNotificationUpdate(newSbn,
-                    "Extending lifetime of notification with smart reply")
+            mNotifUpdater.onInternalNotificationUpdate(
+                newSbn,
+                "Extending lifetime of notification with smart reply",
+            )
             // TODO: Check if the entry was removed due perhaps to an inflation exception?
         }
 
@@ -266,9 +285,9 @@
 
     @VisibleForTesting
     inner class RemoteInputActiveExtender :
-            SelfTrackingLifetimeExtender(TAG, "RemoteInputActive", DEBUG, mMainHandler) {
+        SelfTrackingLifetimeExtender(TAG, "RemoteInputActive", DEBUG, mMainHandler) {
 
         override fun queryShouldExtendLifetime(entry: NotificationEntry): Boolean =
-                mNotificationRemoteInputManager.isRemoteInputActive(entry)
+            mNotificationRemoteInputManager.isRemoteInputActive(entry)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 6d0148a..41419f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -41,7 +41,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype;
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.kotlin.BooleanFlowOperators;
@@ -170,7 +170,7 @@
                     if (entry == null) {
                         return false;
                     }
-                    boolean isTopUnseen = NotificationMinimalismPrototype.isEnabled()
+                    boolean isTopUnseen = NotificationMinimalism.isEnabled()
                             && (mSeenNotificationsInteractor.isTopUnseenNotification(entry)
                                 || mSeenNotificationsInteractor.isTopOngoingNotification(entry));
                     if (isTopUnseen || mHeadsUpManager.isHeadsUpEntry(entry.getKey())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
index 2956432..1babe47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.util.printSection
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -57,25 +57,25 @@
 
     /** Set the entry that is identified as the top ongoing notification. */
     fun setTopOngoingNotification(entry: NotificationEntry?) {
-        if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) return
+        if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return
         notificationListRepository.topOngoingNotificationKey.value = entry?.key
     }
 
     /** Determine if the given notification is the top ongoing notification. */
     fun isTopOngoingNotification(entry: NotificationEntry?): Boolean =
-        if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) false
+        if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) false
         else
             entry != null && notificationListRepository.topOngoingNotificationKey.value == entry.key
 
     /** Set the entry that is identified as the top unseen notification. */
     fun setTopUnseenNotification(entry: NotificationEntry?) {
-        if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) return
+        if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return
         notificationListRepository.topUnseenNotificationKey.value = entry?.key
     }
 
     /** Determine if the given notification is the top unseen notification. */
     fun isTopUnseenNotification(entry: NotificationEntry?): Boolean =
-        if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) false
+        if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) false
         else entry != null && notificationListRepository.topUnseenNotificationKey.value == entry.key
 
     fun dump(pw: IndentingPrintWriter) =
@@ -120,4 +120,29 @@
             // only track the most recent emission, if events are happening faster than they can be
             // consumed
             .conflate()
+
+    fun isLockScreenNotificationMinimalismEnabled(): Flow<Boolean> =
+        secureSettings
+            // emit whenever the setting has changed
+            .observerFlow(
+                UserHandle.USER_ALL,
+                Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM,
+            )
+            // perform a query immediately
+            .onStart { emit(Unit) }
+            // for each change, lookup the new value
+            .map {
+                secureSettings.getIntForUser(
+                    name = Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM,
+                    default = 1,
+                    userHandle = UserHandle.USER_CURRENT,
+                ) == 1
+            }
+            // don't emit anything if nothing has changed
+            .distinctUntilChanged()
+            // perform lookups on the bg thread pool
+            .flowOn(bgDispatcher)
+            // only track the most recent emission, if events are happening faster than they can be
+            // consumed
+            .conflate()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt
index 4c0c461..f1fc275 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/shared/StatusBarRonChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/shared/ModesEmptyShadeFix.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.shared
+package com.android.systemui.statusbar.notification.emptyshade.shared
 
-import com.android.systemui.Flags
+import android.app.Flags
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
-/** Helper for reading or using the status bar ron chips flag state. */
+/** Helper for reading or using the modes_ui_empty_shade flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object StatusBarRonChips {
+object ModesEmptyShadeFix {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
+    const val FLAG_NAME = Flags.FLAG_MODES_UI_EMPTY_SHADE
 
     /** A token used for dependency declaration */
     val token: FlagToken
@@ -33,7 +33,7 @@
     /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.statusBarRonChips()
+        get() = Flags.modesUiEmptyShade()
 
     /**
      * 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/emptyshade/ui/view/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/view/EmptyShadeView.java
new file mode 100644
index 0000000..73477da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/view/EmptyShadeView.java
@@ -0,0 +1,234 @@
+/*
+ * 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.emptyshade.ui.view;
+
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.animation.LaunchableView;
+import com.android.systemui.animation.LaunchableViewDelegate;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+
+import kotlin.Unit;
+
+import java.util.Objects;
+
+public class EmptyShadeView extends StackScrollerDecorView implements LaunchableView {
+
+    private TextView mEmptyText;
+    private TextView mEmptyFooterText;
+
+    private @StringRes int mTextId = R.string.empty_shade_text;
+    private String mTextString;
+
+    private @DrawableRes int mFooterIcon;
+    private @StringRes int mFooterText;
+    // This view is initially gone in the xml.
+    private @Visibility int mFooterVisibility = View.GONE;
+    private int mSize;
+
+    private LaunchableViewDelegate mLaunchableViewDelegate = new LaunchableViewDelegate(this,
+            visibility -> {
+                super.setVisibility(visibility);
+                return Unit.INSTANCE;
+            });
+
+    public EmptyShadeView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mSize = getResources().getDimensionPixelSize(
+                R.dimen.notifications_unseen_footer_icon_size);
+        if (ModesEmptyShadeFix.isEnabled()) {
+            mTextString = getContext().getString(R.string.empty_shade_text);
+        } else {
+            // These will be set by the binder when appropriate if ModesEmptyShadeFix is on.
+            mFooterIcon = R.drawable.ic_friction_lock_closed;
+            mFooterText = R.string.unlock_to_see_notif_text;
+        }
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        mLaunchableViewDelegate.setVisibility(visibility);
+    }
+
+    @Override
+    public void setShouldBlockVisibilityChanges(boolean block) {
+        /* check if */ ModesEmptyShadeFix.isUnexpectedlyInLegacyMode();
+        mLaunchableViewDelegate.setShouldBlockVisibilityChanges(block);
+    }
+
+    @Override
+    public void onActivityLaunchAnimationEnd() {
+        /* check if */ ModesEmptyShadeFix.isUnexpectedlyInLegacyMode();
+    }
+
+    @Override
+    @NonNull
+    public Rect getPaddingForLaunchAnimation() {
+        /* check if */ ModesEmptyShadeFix.isUnexpectedlyInLegacyMode();
+        return new Rect();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mSize = getResources().getDimensionPixelSize(
+                R.dimen.notifications_unseen_footer_icon_size);
+        if (ModesEmptyShadeFix.isEnabled()) {
+            mEmptyText.setText(mTextString);
+        } else {
+            mEmptyText.setText(mTextId);
+        }
+        mEmptyFooterText.setVisibility(mFooterVisibility);
+        setFooterText(mFooterText);
+        setFooterIcon(mFooterIcon);
+    }
+
+    @Override
+    protected View findContentView() {
+        return findViewById(R.id.no_notifications);
+    }
+
+    @Override
+    protected View findSecondaryView() {
+        return findViewById(R.id.no_notifications_footer);
+    }
+
+    /** Update view colors. */
+    public void setTextColors(@ColorInt int onSurface, @ColorInt int onSurfaceVariant) {
+        mEmptyText.setTextColor(onSurfaceVariant);
+        mEmptyFooterText.setTextColor(onSurface);
+        mEmptyFooterText.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+    }
+
+    /** Set the resource ID for the main text shown by the view. */
+    public void setText(@StringRes int text) {
+        ModesEmptyShadeFix.assertInLegacyMode();
+        mTextId = text;
+        mEmptyText.setText(mTextId);
+    }
+
+    /** Set the string for the main text shown by the view. */
+    public void setText(String text) {
+        if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode() || Objects.equals(mTextString, text)) {
+            return;
+        }
+        mTextString = text;
+        mEmptyText.setText(text);
+    }
+
+    /** Visibility for the footer (the additional icon+text shown below the main text). */
+    public void setFooterVisibility(@Visibility int visibility) {
+        if (ModesEmptyShadeFix.isEnabled() && mFooterVisibility == visibility) {
+            return; // nothing to change
+        }
+        mFooterVisibility = visibility;
+        setSecondaryVisible(/* visible = */ visibility == View.VISIBLE,
+                /* animate = */false,
+                /* onAnimationEnded = */ null);
+    }
+
+    /** Text resource ID for the footer (the additional icon+text shown below the main text). */
+    public void setFooterText(@StringRes int text) {
+        if (ModesEmptyShadeFix.isEnabled() && mFooterText == text) {
+            return; // nothing to change
+        }
+        mFooterText = text;
+        if (text != 0) {
+            mEmptyFooterText.setText(mFooterText);
+        } else {
+            mEmptyFooterText.setText(null);
+        }
+    }
+
+    /** Icon resource ID for the footer (the additional icon+text shown below the main text). */
+    public void setFooterIcon(@DrawableRes int icon) {
+        if (ModesEmptyShadeFix.isEnabled() && mFooterIcon == icon) {
+            return; // nothing to change
+        }
+        mFooterIcon = icon;
+        Drawable drawable;
+        if (icon == 0) {
+            drawable = null;
+        } else {
+            drawable = getResources().getDrawable(icon);
+            drawable.setBounds(0, 0, mSize, mSize);
+        }
+        mEmptyFooterText.setCompoundDrawablesRelative(drawable, null, null, null);
+    }
+
+    /** Get resource ID for main text. */
+    @StringRes
+    public int getTextResource() {
+        ModesEmptyShadeFix.assertInLegacyMode();
+        return mTextId;
+    }
+
+    /** Get resource ID for footer text. */
+    @StringRes
+    public int getFooterTextResource() {
+        ModesEmptyShadeFix.assertInLegacyMode();
+        return mFooterText;
+    }
+
+    /** Get resource ID for footer icon. */
+    @DrawableRes
+    public int getFooterIconResource() {
+        ModesEmptyShadeFix.assertInLegacyMode();
+        return mFooterIcon;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mEmptyText = (TextView) findContentView();
+        mEmptyFooterText = (TextView) findSecondaryView();
+        mEmptyFooterText.setCompoundDrawableTintList(mEmptyFooterText.getTextColors());
+    }
+
+    @Override
+    @NonNull
+    public ExpandableViewState createExpandableViewState() {
+        return new EmptyShadeViewState();
+    }
+
+    public class EmptyShadeViewState extends ExpandableViewState {
+        @Override
+        public void applyToView(View view) {
+            super.applyToView(view);
+            if (view instanceof EmptyShadeView) {
+                EmptyShadeView emptyShadeView = (EmptyShadeView) view;
+                boolean visible = this.clipTopAmount <= mEmptyText.getPaddingTop() * 0.6f;
+                emptyShadeView.setContentVisibleAnimated(visible && emptyShadeView.isVisible());
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewbinder/EmptyShadeViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewbinder/EmptyShadeViewBinder.kt
new file mode 100644
index 0000000..7f1b043
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewbinder/EmptyShadeViewBinder.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.statusbar.notification.emptyshade.ui.viewbinder
+
+import android.view.View
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView
+import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+object EmptyShadeViewBinder {
+    suspend fun bind(
+        view: EmptyShadeView,
+        viewModel: EmptyShadeViewModel,
+        notificationActivityStarter: NotificationActivityStarter,
+    ) = coroutineScope {
+        launch { viewModel.text.collect { view.setText(it) } }
+
+        launch {
+            viewModel.onClick.collect { settingsIntent ->
+                val onClickListener = { view: View ->
+                    notificationActivityStarter.startSettingsIntent(view, settingsIntent)
+                }
+                view.setOnClickListener(onClickListener)
+            }
+        }
+
+        launch { bindFooter(view, viewModel) }
+    }
+
+    private suspend fun bindFooter(view: EmptyShadeView, viewModel: EmptyShadeViewModel) =
+        coroutineScope {
+            // Bind the resource IDs
+            view.setFooterText(viewModel.footer.messageId)
+            view.setFooterIcon(viewModel.footer.iconId)
+
+            launch {
+                viewModel.footer.isVisible.collect { visible ->
+                    view.setFooterVisibility(if (visible) View.VISIBLE else View.GONE)
+                }
+            }
+        }
+}
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
new file mode 100644
index 0000000..8c8f200
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.emptyshade.ui.viewmodel
+
+import android.content.Context
+import android.icu.text.MessageFormat
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.modes.shared.ModesUi
+import com.android.systemui.res.R
+import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
+import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterMessageViewModel
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.Locale
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/**
+ * ViewModel for the empty shade (aka the "No notifications" text shown when there are no
+ * notifications.
+ */
+class EmptyShadeViewModel
+@AssistedInject
+constructor(
+    private val context: Context,
+    zenModeInteractor: ZenModeInteractor,
+    seenNotificationsInteractor: SeenNotificationsInteractor,
+    notificationSettingsInteractor: NotificationSettingsInteractor,
+    dumpManager: DumpManager,
+) : FlowDumperImpl(dumpManager) {
+    val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
+        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+            flowOf(false)
+        } else {
+            zenModeInteractor.areNotificationsHiddenInShade.dumpWhileCollecting(
+                "areNotificationsHiddenInShade"
+            )
+        }
+    }
+
+    val hasFilteredOutSeenNotifications: StateFlow<Boolean> by lazy {
+        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+            MutableStateFlow(false)
+        } else {
+            seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpValue(
+                "hasFilteredOutSeenNotifications"
+            )
+        }
+    }
+
+    val text: Flow<String> by lazy {
+        if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
+            flowOf(context.getString(R.string.empty_shade_text))
+        } else {
+            // Note: Flag modes_ui_empty_shade includes two pieces: refactoring the empty shade to
+            // 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 ->
+                    // Create a string that is either "No notifications" if no modes are filtering
+                    // them out, or something like "Notifications paused by SomeMode" otherwise.
+                    val msgFormat =
+                        MessageFormat(
+                            context.getString(R.string.modes_suppressing_shade_text),
+                            Locale.getDefault(),
+                        )
+                    val count = modes.count()
+                    val args: MutableMap<String, Any> = HashMap()
+                    args["count"] = count
+                    if (count >= 1) {
+                        args["mode"] = modes[0].name
+                    }
+                    msgFormat.format(args)
+                }
+            } else {
+                areNotificationsHiddenInShade.map { areNotificationsHiddenInShade ->
+                    if (areNotificationsHiddenInShade) {
+                        context.getString(R.string.dnd_suppressing_shade_text)
+                    } else {
+                        context.getString(R.string.empty_shade_text)
+                    }
+                }
+            }
+        }
+    }
+
+    val footer: FooterMessageViewModel by lazy {
+        ModesEmptyShadeFix.assertInNewMode()
+        FooterMessageViewModel(
+            messageId = R.string.unlock_to_see_notif_text,
+            iconId = R.drawable.ic_friction_lock_closed,
+            isVisible = hasFilteredOutSeenNotifications,
+        )
+    }
+
+    val onClick: Flow<SettingsIntent> by lazy {
+        ModesEmptyShadeFix.assertInNewMode()
+        combine(
+            zenModeInteractor.modesHidingNotifications,
+            notificationSettingsInteractor.isNotificationHistoryEnabled,
+        ) { modes, isNotificationHistoryEnabled ->
+            if (modes.isNotEmpty()) {
+                if (modes.size == 1) {
+                    SettingsIntent.forModeSettings(modes[0].id)
+                } else {
+                    SettingsIntent.forModesSettings()
+                }
+            } else {
+                if (isNotificationHistoryEnabled) {
+                    SettingsIntent.forNotificationHistory()
+                } else {
+                    SettingsIntent.forNotificationSettings()
+                }
+            }
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): EmptyShadeViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 920541d..22bec5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -19,6 +19,8 @@
 import android.view.View
 import androidx.lifecycle.lifecycleScope
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
 import com.android.systemui.util.ui.isAnimating
@@ -36,6 +38,7 @@
         clearAllNotifications: View.OnClickListener,
         launchNotificationSettings: View.OnClickListener,
         launchNotificationHistory: View.OnClickListener,
+        notificationActivityStarter: NotificationActivityStarter,
     ): DisposableHandle {
         return footer.repeatWhenAttached {
             lifecycleScope.launch {
@@ -45,6 +48,7 @@
                     clearAllNotifications,
                     launchNotificationSettings,
                     launchNotificationHistory,
+                    notificationActivityStarter,
                 )
             }
         }
@@ -56,6 +60,7 @@
         clearAllNotifications: View.OnClickListener,
         launchNotificationSettings: View.OnClickListener,
         launchNotificationHistory: View.OnClickListener,
+        notificationActivityStarter: NotificationActivityStarter,
     ) = coroutineScope {
         launch { bindClearAllButton(footer, viewModel, clearAllNotifications) }
         launch {
@@ -64,6 +69,7 @@
                 viewModel,
                 launchNotificationSettings,
                 launchNotificationHistory,
+                notificationActivityStarter,
             )
         }
         launch { bindMessage(footer, viewModel) }
@@ -113,13 +119,23 @@
         viewModel: FooterViewModel,
         launchNotificationSettings: View.OnClickListener,
         launchNotificationHistory: View.OnClickListener,
+        notificationActivityStarter: NotificationActivityStarter,
     ) = coroutineScope {
         launch {
-            viewModel.manageButtonShouldLaunchHistory.collect { shouldLaunchHistory ->
-                if (shouldLaunchHistory) {
-                    footer.setManageButtonClickListener(launchNotificationHistory)
-                } else {
-                    footer.setManageButtonClickListener(launchNotificationSettings)
+            if (ModesEmptyShadeFix.isEnabled) {
+                viewModel.manageOrHistoryButtonClick.collect { settingsIntent ->
+                    val onClickListener = { view: View ->
+                        notificationActivityStarter.startSettingsIntent(view, settingsIntent)
+                    }
+                    footer.setManageButtonClickListener(onClickListener)
+                }
+            } else {
+                viewModel.manageButtonShouldLaunchHistory.collect { shouldLaunchHistory ->
+                    if (shouldLaunchHistory) {
+                        footer.setManageButtonClickListener(launchNotificationHistory)
+                    } else {
+                        footer.setManageButtonClickListener(launchNotificationSettings)
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index 90fb728..a3f4cd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -16,12 +16,17 @@
 
 package com.android.systemui.statusbar.notification.footer.ui.viewmodel
 
+import android.content.Intent
+import android.provider.Settings
+import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
+import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.util.kotlin.sample
@@ -80,7 +85,7 @@
                         combine(
                                 shadeInteractor.isShadeFullyExpanded,
                                 shadeInteractor.isShadeTouchable,
-                                ::Pair
+                                ::Pair,
                             )
                             .onStart { emit(Pair(false, false)) }
                     ) { clearAllButtonVisible, (isShadeFullyExpanded, animationsEnabled) ->
@@ -93,8 +98,28 @@
     val manageButtonShouldLaunchHistory =
         notificationSettingsInteractor.isNotificationHistoryEnabled
 
+    // TODO(b/366003631): When inlining the flag, consider adding this to FooterButtonViewModel.
+    val manageOrHistoryButtonClick: Flow<SettingsIntent> by lazy {
+        if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
+            flowOf(SettingsIntent(Intent(Settings.ACTION_NOTIFICATION_SETTINGS)))
+        } else {
+            notificationSettingsInteractor.isNotificationHistoryEnabled.map {
+                isNotificationHistoryEnabled ->
+                if (isNotificationHistoryEnabled) {
+                    SettingsIntent.forNotificationHistory(
+                        cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
+                    )
+                } else {
+                    SettingsIntent.forNotificationSettings(
+                        cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
+                    )
+                }
+            }
+        }
+    }
+
     private val manageOrHistoryButtonText: Flow<Int> =
-        manageButtonShouldLaunchHistory.map { shouldLaunchHistory ->
+        notificationSettingsInteractor.isNotificationHistoryEnabled.map { shouldLaunchHistory ->
             if (shouldLaunchHistory) R.string.manage_notifications_history_text
             else R.string.manage_notifications_text
         }
@@ -128,7 +153,7 @@
                     activeNotificationsInteractor.get(),
                     notificationSettingsInteractor.get(),
                     seenNotificationsInteractor.get(),
-                    shadeInteractor.get()
+                    shadeInteractor.get(),
                 )
             )
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 560028c..7b6a2cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -444,11 +444,9 @@
                 if (onFinishedRunnable != null) {
                     onFinishedRunnable.run();
                 }
-                if (mRunWithoutInterruptions) {
-                    enableAppearDrawing(false);
-                }
 
                 // We need to reset the View state, even if the animation was cancelled
+                enableAppearDrawing(false);
                 onAppearAnimationFinished(isAppearing);
 
                 if (mRunWithoutInterruptions) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 291dc13..cd228e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -72,14 +72,24 @@
     }
 
     /**
+     * See {@link #setVisible(boolean, boolean, Consumer)}.
+     */
+    public void setVisible(boolean visible, boolean animate) {
+        setVisible(visible, animate, null /* onAnimationEnded */);
+    }
+
+    /**
      * Make this view visible. If {@code false} is passed, the view will fade out its content
      * and set the view Visibility to GONE. If only the content should be changed,
      * {@link #setContentVisibleAnimated(boolean)} can be used.
      *
      * @param visible True if the contents should be visible.
      * @param animate True if we should fade to new visibility.
+     * @param onAnimationEnded Callback to run after visibility updates, takes a boolean as a
+     *                         parameter that represents whether the animation was cancelled.
      */
-    public void setVisible(boolean visible, boolean animate) {
+    public void setVisible(boolean visible, boolean animate,
+            Consumer<Boolean> onAnimationEnded) {
         if (mIsVisible != visible) {
             mIsVisible = visible;
             if (animate) {
@@ -90,10 +100,10 @@
                 } else {
                     setWillBeGone(true);
                 }
-                setContentVisible(visible, true /* animate */, null /* onAnimationEnded */);
+                setContentVisible(visible, true /* animate */, onAnimationEnded);
             } else {
                 setVisibility(visible ? VISIBLE : GONE);
-                setContentVisible(visible, false /* animate */, null /* onAnimationEnded */);
+                setContentVisible(visible, false /* animate */, onAnimationEnded);
                 setWillBeGone(false);
                 notifyHeightChanged(false /* needsAnimation */);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalism.kt
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalism.kt
index 06f3db5..70bb272 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalism.kt
@@ -17,23 +17,23 @@
 package com.android.systemui.statusbar.notification.shared
 
 import android.os.SystemProperties
-import com.android.systemui.Flags
+import com.android.server.notification.Flags
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
 /** Helper for reading or using the minimalism prototype flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object NotificationMinimalismPrototype {
-    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE
+object NotificationMinimalism {
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM
 
     /** A token used for dependency declaration */
     val token: FlagToken
         get() = FlagToken(FLAG_NAME, isEnabled)
 
-    /** Is the heads-up cycling animation enabled */
+    /** Is the notification minimalism enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.notificationMinimalismPrototype()
+        get() = Flags.notificationMinimalism()
 
     /**
      * The prototype will (by default) use a promoter to ensure that the top unseen notification is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 1431b28..129d4ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -88,6 +88,8 @@
     private ExpandableView mLastVisibleBackgroundChild;
     private float mCurrentScrollVelocity;
     private int mStatusBarState;
+    private boolean mShowingStackOnLockscreen;
+    private float mLockscreenStackFadeInProgress;
     private float mExpandingVelocity;
     private boolean mPanelTracking;
     private boolean mExpansionChanging;
@@ -222,6 +224,7 @@
      * @param isSwipingUp Whether we are swiping up.
      */
     public void setSwipingUp(boolean isSwipingUp) {
+        SceneContainerFlag.assertInLegacyMode();
         if (!isSwipingUp && mIsSwipingUp) {
             // Just stopped swiping up.
             mIsFlingRequiredAfterLockScreenSwipeUp = true;
@@ -240,6 +243,7 @@
      * @param isFlinging Whether we are flinging the shade open or closed.
      */
     public void setFlinging(boolean isFlinging) {
+        SceneContainerFlag.assertInLegacyMode();
         if (isOnKeyguard() && !isFlinging && mIsFlinging) {
             // Just stopped flinging.
             mIsFlingRequiredAfterLockScreenSwipeUp = false;
@@ -624,6 +628,26 @@
         return mStatusBarState == StatusBarState.KEYGUARD;
     }
 
+    public boolean isShowingStackOnLockscreen() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return false;
+        return mShowingStackOnLockscreen;
+    }
+
+    public void setShowingStackOnLockscreen(boolean showingStackOnLockscreen) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        mShowingStackOnLockscreen = showingStackOnLockscreen;
+    }
+
+    public float getLockscreenStackFadeInProgress() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+        return mLockscreenStackFadeInProgress;
+    }
+
+    public void setLockscreenStackFadeInProgress(float lockscreenStackFadeInProgress) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        mLockscreenStackFadeInProgress = lockscreenStackFadeInProgress;
+    }
+
     public void setStatusBarState(int statusBarState) {
         if (mStatusBarState != StatusBarState.KEYGUARD) {
             mIsFlingRequiredAfterLockScreenSwipeUp = false;
@@ -695,6 +719,7 @@
      * @return Whether we need to do a fling down after swiping up on lockscreen.
      */
     public boolean isFlingingAfterSwipeUpOnLockscreen() {
+        SceneContainerFlag.assertInLegacyMode();
         return mIsFlinging && mIsFlingRequiredAfterLockScreenSwipeUp;
     }
 
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 48796d8..b108388 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
@@ -634,8 +634,10 @@
     }
 
     private View inflateDivider() {
-        return LayoutInflater.from(mContext).inflate(
+        View divider = LayoutInflater.from(mContext).inflate(
                 R.layout.notification_children_divider, this, false);
+        divider.setAlpha(0f);
+        return divider;
     }
 
     /**
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 e7c67f9..b466bf0 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
@@ -22,7 +22,6 @@
 
 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.newAodTransition;
 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;
@@ -94,7 +93,6 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.QSHeaderBoundsProvider;
 import com.android.systemui.shade.TouchLogger;
-import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
@@ -106,6 +104,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
+import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -568,6 +568,7 @@
     private boolean mHasFilteredOutSeenNotifications;
     @Nullable private SplitShadeStateController mSplitShadeStateController = null;
     private boolean mIsSmallLandscapeLockscreenEnabled = false;
+    private boolean mSuppressHeightUpdates;
 
     /** Pass splitShadeStateController to view and update split shade */
     public void passSplitShadeStateController(SplitShadeStateController splitShadeStateController) {
@@ -687,7 +688,9 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        inflateEmptyShadeView();
+        if (!ModesEmptyShadeFix.isEnabled()) {
+            inflateEmptyShadeView();
+        }
         if (!FooterViewRefactor.isEnabled()) {
             inflateFooterView();
         }
@@ -730,7 +733,9 @@
             inflateFooterView();
             updateFooter();
         }
-        inflateEmptyShadeView();
+        if (!ModesEmptyShadeFix.isEnabled()) {
+            inflateEmptyShadeView();
+        }
         mSectionsManager.reinflateViews();
     }
 
@@ -1260,6 +1265,7 @@
     @Override
     public void setHeadsUpBottom(float headsUpBottom) {
         mAmbientState.setHeadsUpBottom(headsUpBottom);
+        mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
     }
 
     @Override
@@ -1453,9 +1459,13 @@
      * 2) Swiping up on lockscreen or flinging down after swipe up
      */
     private boolean shouldSkipHeightUpdate() {
-        return mAmbientState.isOnKeyguard()
-                && (mAmbientState.isSwipingUp()
-                || mAmbientState.isFlingingAfterSwipeUpOnLockscreen());
+        if (SceneContainerFlag.isEnabled()) {
+            return mSuppressHeightUpdates;
+        } else {
+            return mAmbientState.isOnKeyguard()
+                    && (mAmbientState.isSwipingUp()
+                    || mAmbientState.isFlingingAfterSwipeUpOnLockscreen());
+        }
     }
 
     /**
@@ -1548,15 +1558,6 @@
 
     @VisibleForTesting
     public void updateInterpolatedStackHeight(float endHeight, float fraction) {
-        if (!newAodTransition()) {
-            // During the (AOD<=>LS) transition where dozeAmount is changing,
-            // apply dozeAmount to stack height instead of expansionFraction
-            // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep)
-            final float dozeAmount = mAmbientState.getDozeAmount();
-            if (0f < dozeAmount && dozeAmount < 1f) {
-                fraction = 1f - dozeAmount;
-            }
-        }
         mAmbientState.setInterpolatedStackHeight(
                 MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION,
                         endHeight, fraction));
@@ -4844,6 +4845,8 @@
     /** Trigger an update for the empty shade resources and visibility. */
     public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade,
             boolean hasFilteredOutSeenNotifications) {
+        ModesEmptyShadeFix.assertInLegacyMode();
+
         mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
 
         if (areNotificationsHiddenInShade) {
@@ -4862,6 +4865,8 @@
             @StringRes int newTextRes,
             @StringRes int newFooterTextRes,
             @DrawableRes int newFooterIconRes) {
+        ModesEmptyShadeFix.assertInLegacyMode();
+
         int oldTextRes = mEmptyShadeView.getTextResource();
         if (oldTextRes != newTextRes) {
             mEmptyShadeView.setText(newTextRes);
@@ -4883,6 +4888,9 @@
 
     public boolean isEmptyShadeViewVisible() {
         SceneContainerFlag.assertInLegacyMode();
+        if (mEmptyShadeView == null) {
+            return false;
+        }
         return mEmptyShadeView.isVisible();
     }
 
@@ -5339,6 +5347,19 @@
         updateDismissBehavior();
     }
 
+    @Override
+    public void setShowingStackOnLockscreen(boolean showingStackOnLockscreen) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        mAmbientState.setShowingStackOnLockscreen(showingStackOnLockscreen);
+    }
+
+    @Override
+    public void setAlphaForLockscreenFadeIn(float alphaForLockscreenFadeIn) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        mAmbientState.setLockscreenStackFadeInProgress(alphaForLockscreenFadeIn);
+        requestChildrenUpdate();
+    }
+
     void setUpcomingStatusBarState(int upcomingStatusBarState) {
         FooterViewRefactor.assertInLegacyMode();
         mUpcomingStatusBarState = upcomingStatusBarState;
@@ -5370,7 +5391,7 @@
 
     public float getOpeningHeight() {
         SceneContainerFlag.assertInLegacyMode();
-        if (mEmptyShadeView.getVisibility() == GONE) {
+        if (mEmptyShadeView == null || mEmptyShadeView.getVisibility() == GONE) {
             return getMinExpansionHeight();
         } else {
             return FooterViewRefactor.isEnabled() ? getAppearEndPosition()
@@ -5383,6 +5404,7 @@
     }
 
     public void setPanelFlinging(boolean flinging) {
+        SceneContainerFlag.assertInLegacyMode();
         mAmbientState.setFlinging(flinging);
         if (!flinging) {
             // re-calculate the stack height which was frozen while flinging
@@ -5390,6 +5412,12 @@
         }
     }
 
+    @Override
+    public void suppressHeightUpdates(boolean suppress) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        mSuppressHeightUpdates = suppress;
+    }
+
     public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
         mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
     }
@@ -5719,6 +5747,8 @@
     }
 
     private void inflateEmptyShadeView() {
+        ModesEmptyShadeFix.assertInLegacyMode();
+
         EmptyShadeView oldView = mEmptyShadeView;
         EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
                 R.layout.status_bar_no_notifications, this, false);
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 dad6894..7b02d0c 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
@@ -380,7 +380,7 @@
             new StatusBarStateController.StateListener() {
                 @Override
                 public void onStatePreChange(int oldState, int newState) {
-                    if (oldState == StatusBarState.SHADE_LOCKED
+                    if (!SceneContainerFlag.isEnabled() && oldState == StatusBarState.SHADE_LOCKED
                             && newState == KEYGUARD) {
                         mView.requestAnimateEverything();
                     }
@@ -1439,6 +1439,7 @@
     }
 
     public void setPanelFlinging(boolean flinging) {
+        SceneContainerFlag.assertInLegacyMode();
         mView.setPanelFlinging(flinging);
     }
 
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 06222fd..3bc5495 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,7 +29,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.Compile
 import com.android.systemui.util.children
@@ -74,7 +74,7 @@
 
     /** Whether we allow keyguard to show less important notifications above the shelf. */
     private val limitLockScreenToOneImportant
-        get() = NotificationMinimalismPrototype.isEnabled
+        get() = NotificationMinimalism.isEnabled
 
     /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
     private var dividerHeight by notNull<Float>()
@@ -406,7 +406,7 @@
     fun updateResources() {
         maxKeyguardNotifications =
             infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count))
-        maxNotificationsExcludesMedia = NotificationMinimalismPrototype.isEnabled
+        maxNotificationsExcludesMedia = NotificationMinimalism.isEnabled
 
         dividerHeight =
             max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index cccac4b..e0b0ccd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -31,9 +31,9 @@
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
-import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -148,12 +148,18 @@
             if (isHunGoingToShade) {
                 // Keep 100% opacity for heads up notification going to shade.
                 viewState.setAlpha(1f);
-            } else if (ambientState.isOnKeyguard()) {
+            } else if ((!SceneContainerFlag.isEnabled() && ambientState.isOnKeyguard())
+                    || ambientState.isShowingStackOnLockscreen()) {
                 // Adjust alpha for wakeup to lockscreen.
                 if (view.isHeadsUpState()) {
                     // Pulsing HUN should be visible on AOD and stay visible during 
                     // AOD=>lockscreen transition
                     viewState.setAlpha(1f - ambientState.getHideAmount());
+                } else if (SceneContainerFlag.isEnabled()) {
+                    // Take into account scene container-specific Lockscreen fade-in progress
+                    float fadeAlpha = ambientState.getLockscreenStackFadeInProgress();
+                    float dozeAlpha = 1f - ambientState.getDozeAmount();
+                    viewState.setAlpha(Math.min(dozeAlpha, fadeAlpha));
                 } else {
                     // Normal notifications are hidden on AOD and should fade in during 
                     // AOD=>lockscreen transition
@@ -677,22 +683,30 @@
         );
         if (view instanceof FooterView) {
             if (FooterViewRefactor.isEnabled()) {
-                // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
-                //  already, so we shouldn't need to use ambientState here. However, currently it
-                //  doesn't get updated quickly enough and can cause the footer to flash when
-                //  closing the shade. As such, we temporarily also check the ambientState directly.
-                if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
-                    // Note: This is no longer necessary in flexiglass.
-                    if (!SceneContainerFlag.isEnabled()) {
-                        viewState.hidden = true;
-                    }
-                } else {
-                    final float footerEnd = algorithmState.mCurrentExpandedYPosition
-                            + view.getIntrinsicHeight();
-                    final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
+                if (SceneContainerFlag.isEnabled()) {
+                    final float footerEnd =
+                            stackTop + viewState.getYTranslation() + view.getIntrinsicHeight();
+                    final boolean noSpaceForFooter = footerEnd > ambientState.getStackCutoff();
                     ((FooterView.FooterViewState) viewState).hideContent =
                             noSpaceForFooter || (ambientState.isClearAllInProgress()
                                     && !hasNonClearableNotifs(algorithmState));
+                } else {
+                    // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
+                    //  already, so we shouldn't need to use ambientState here. However,
+                    //  currently it doesn't get updated quickly enough and can cause the footer to
+                    //  flash when closing the shade. As such, we temporarily also check the
+                    //  ambientState directly.
+                    if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
+                        viewState.hidden = true;
+                    } else {
+                        final float footerEnd = algorithmState.mCurrentExpandedYPosition
+                                + view.getIntrinsicHeight();
+                        final boolean noSpaceForFooter =
+                                footerEnd > ambientState.getStackEndHeight();
+                        ((FooterView.FooterViewState) viewState).hideContent =
+                                noSpaceForFooter || (ambientState.isClearAllInProgress()
+                                        && !hasNonClearableNotifs(algorithmState));
+                    }
                 }
             } else {
                 final boolean shadeClosed = !ambientState.isShadeExpanded();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index f6722a4..c0f1a56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -31,6 +31,9 @@
     /** The alpha of the shade in order to show brightness. */
     val alphaForBrightnessMirror = MutableStateFlow(1f)
 
+    /** The alpha of the Notification Stack for lockscreen fade-in */
+    val alphaForLockscreenFadeIn = MutableStateFlow(0f)
+
     /**
      * The bounds of the notification shade scrim / container in the current scene.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 756cd87..32e092b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -56,10 +56,9 @@
 
     /** The rounding of the notification stack. */
     val shadeScrimRounding: Flow<ShadeScrimRounding> =
-        combine(
-                shadeInteractor.shadeMode,
-                isExpandingFromHeadsUp,
-            ) { shadeMode, isExpandingFromHeadsUp ->
+        combine(shadeInteractor.shadeMode, isExpandingFromHeadsUp) {
+                shadeMode,
+                isExpandingFromHeadsUp ->
                 ShadeScrimRounding(
                     isTopRounded = !(shadeMode == ShadeMode.Split && isExpandingFromHeadsUp),
                     isBottomRounded = shadeMode != ShadeMode.Single,
@@ -71,6 +70,10 @@
     val alphaForBrightnessMirror: StateFlow<Float> =
         placeholderRepository.alphaForBrightnessMirror.asStateFlow()
 
+    /** The alpha of the Notification Stack for lockscreen fade-in */
+    val alphaForLockscreenFadeIn: StateFlow<Float> =
+        placeholderRepository.alphaForLockscreenFadeIn.asStateFlow()
+
     /** The height of the keyguard's available space bounds */
     val constrainedAvailableSpace: StateFlow<Int> =
         placeholderRepository.constrainedAvailableSpace.asStateFlow()
@@ -99,7 +102,7 @@
     val shouldCloseGuts: Flow<Boolean> =
         combine(
             sceneInteractor.isSceneContainerUserInputOngoing,
-            viewHeightRepository.isCurrentGestureInGuts
+            viewHeightRepository.isCurrentGestureInGuts,
         ) { isUserInputOngoing, isCurrentGestureInGuts ->
             isUserInputOngoing && !isCurrentGestureInGuts
         }
@@ -109,6 +112,11 @@
         placeholderRepository.alphaForBrightnessMirror.value = alpha
     }
 
+    /** Sets the alpha to apply to the NSSL for fade-in on lockscreen */
+    fun setAlphaForLockscreenFadeIn(alpha: Float) {
+        placeholderRepository.alphaForLockscreenFadeIn.value = alpha
+    }
+
     /** Sets the position of the notification stack in the current scene. */
     fun setShadeScrimBounds(bounds: ShadeScrimBounds?) {
         check(bounds == null || bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 41c0293..dbe81c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -89,6 +89,12 @@
     /** sets the current QS expand fraction */
     fun setQsExpandFraction(expandFraction: Float)
 
+    /** set whether we are idle on the lockscreen scene */
+    fun setShowingStackOnLockscreen(showingStackOnLockscreen: Boolean)
+
+    /** set the alpha from 0-1f of stack fade-in on lockscreen */
+    fun setAlphaForLockscreenFadeIn(alphaForLockscreenFadeIn: Float)
+
     /** Sets whether the view is displayed in doze mode. */
     fun setDozing(dozing: Boolean)
 
@@ -118,4 +124,7 @@
 
     /** @see addHeadsUpHeightChangedListener */
     fun removeHeadsUpHeightChangedListener(runnable: Runnable)
+
+    /** Sets whether updates to the stack are are suppressed. */
+    fun suppressHeightUpdates(suppress: Boolean)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index dc9615c..ebae235 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewbinder
 
 import android.view.LayoutInflater
+import android.view.View
 import androidx.lifecycle.lifecycleScope
 import com.android.app.tracing.TraceUtils.traceAsync
 import com.android.internal.logging.MetricsLogger
@@ -25,6 +26,7 @@
 import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.repeatWhenAttachedToWindow
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -32,6 +34,10 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
 import com.android.systemui.statusbar.notification.dagger.SilentHeader
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
+import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView
+import com.android.systemui.statusbar.notification.emptyshade.ui.viewbinder.EmptyShadeViewBinder
+import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
@@ -49,6 +55,7 @@
 import com.android.systemui.util.kotlin.awaitCancellationThenDispose
 import com.android.systemui.util.kotlin.getOrNull
 import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.stopAnimating
 import com.android.systemui.util.ui.value
 import java.util.Optional
 import javax.inject.Inject
@@ -84,7 +91,7 @@
 
     fun bindWhileAttached(
         view: NotificationStackScrollLayout,
-        viewController: NotificationStackScrollLayoutController
+        viewController: NotificationStackScrollLayoutController,
     ) {
         val shelf =
             LayoutInflater.from(view.context)
@@ -103,7 +110,13 @@
                     val hasNonClearableSilentNotifications: StateFlow<Boolean> =
                         viewModel.hasNonClearableSilentNotifications.stateIn(this)
                     launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) }
-                    launch { bindEmptyShade(view) }
+                    launch {
+                        if (ModesEmptyShadeFix.isEnabled) {
+                            reinflateAndBindEmptyShade(view)
+                        } else {
+                            bindEmptyShadeLegacy(viewModel.emptyShadeViewFactory.create(), view)
+                        }
+                    }
                     launch {
                         bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications)
                     }
@@ -121,17 +134,12 @@
     }
 
     private suspend fun bindShelf(shelf: NotificationShelf) {
-        NotificationShelfViewBinder.bind(
-            shelf,
-            viewModel.shelf,
-            falsingManager,
-            nicBinder,
-        )
+        NotificationShelfViewBinder.bind(shelf, viewModel.shelf, falsingManager, nicBinder)
     }
 
     private suspend fun reinflateAndBindFooter(
         parentView: NotificationStackScrollLayout,
-        hasNonClearableSilentNotifications: StateFlow<Boolean>
+        hasNonClearableSilentNotifications: StateFlow<Boolean>,
     ) {
         viewModel.footer.getOrNull()?.let { footerViewModel ->
             // The footer needs to be re-inflated every time the theme or the font size changes.
@@ -149,7 +157,7 @@
                             footerView,
                             footerViewModel,
                             parentView,
-                            hasNonClearableSilentNotifications
+                            hasNonClearableSilentNotifications,
                         )
                     }
                 }
@@ -163,13 +171,13 @@
         footerView: FooterView,
         footerViewModel: FooterViewModel,
         parentView: NotificationStackScrollLayout,
-        hasNonClearableSilentNotifications: StateFlow<Boolean>
+        hasNonClearableSilentNotifications: StateFlow<Boolean>,
     ): Unit = coroutineScope {
         val disposableHandle =
             FooterViewBinder.bindWhileAttached(
                 footerView,
                 footerViewModel,
-                clearAllNotifications = {
+                {
                     clearAllNotifications(
                         parentView,
                         // Hide the silent section header (if present) if there will be
@@ -177,16 +185,9 @@
                         hideSilentSection = !hasNonClearableSilentNotifications.value,
                     )
                 },
-                launchNotificationSettings = { view ->
-                    notificationActivityStarter
-                        .get()
-                        .startHistoryIntent(view, /* showHistory= */ false)
-                },
-                launchNotificationHistory = { view ->
-                    notificationActivityStarter
-                        .get()
-                        .startHistoryIntent(view, /* showHistory= */ true)
-                },
+                launchNotificationSettings,
+                launchNotificationHistory,
+                notificationActivityStarter.get(),
             )
         if (SceneContainerFlag.isEnabled) {
             launch {
@@ -194,7 +195,9 @@
                     footerView.setVisible(
                         /* visible = */ animatedVisibility.value,
                         /* animate = */ animatedVisibility.isAnimating,
-                    )
+                    ) {
+                        animatedVisibility.stopAnimating()
+                    }
                 }
             }
         } else {
@@ -211,20 +214,70 @@
         disposableHandle.awaitCancellationThenDispose()
     }
 
-    private suspend fun bindEmptyShade(parentView: NotificationStackScrollLayout) {
+    private val launchNotificationSettings: (View) -> Unit = { view: View ->
+        notificationActivityStarter.get().startHistoryIntent(view, /* showHistory= */ false)
+    }
+
+    private val launchNotificationHistory: (View) -> Unit = { view ->
+        notificationActivityStarter.get().startHistoryIntent(view, /* showHistory= */ true)
+    }
+
+    private suspend fun reinflateAndBindEmptyShade(parentView: NotificationStackScrollLayout) {
+        ModesEmptyShadeFix.assertInNewMode()
+        // The empty shade needs to be re-inflated every time the theme or the font size
+        // changes.
+        configuration
+            .inflateLayout<EmptyShadeView>(
+                R.layout.status_bar_no_notifications,
+                parentView,
+                attachToRoot = false,
+            )
+            .flowOn(backgroundDispatcher)
+            .collectLatest { emptyShadeView: EmptyShadeView ->
+                traceAsync("bind EmptyShadeView") {
+                    parentView.setEmptyShadeView(emptyShadeView)
+                    bindEmptyShade(emptyShadeView, viewModel.emptyShadeViewFactory.create())
+                }
+            }
+    }
+
+    private suspend fun bindEmptyShadeLegacy(
+        emptyShadeViewModel: EmptyShadeViewModel,
+        parentView: NotificationStackScrollLayout,
+    ) {
+        ModesEmptyShadeFix.assertInLegacyMode()
         combine(
                 viewModel.shouldShowEmptyShadeView,
-                viewModel.areNotificationsHiddenInShade,
-                viewModel.hasFilteredOutSeenNotifications,
-                ::Triple
+                emptyShadeViewModel.areNotificationsHiddenInShade,
+                emptyShadeViewModel.hasFilteredOutSeenNotifications,
+                ::Triple,
             )
             .collect { (shouldShow, areNotifsHidden, hasFilteredNotifs) ->
-                parentView.updateEmptyShadeView(
-                    shouldShow,
-                    areNotifsHidden,
-                    hasFilteredNotifs,
+                parentView.updateEmptyShadeView(shouldShow, areNotifsHidden, hasFilteredNotifs)
+            }
+    }
+
+    private suspend fun bindEmptyShade(
+        emptyShadeView: EmptyShadeView,
+        emptyShadeViewModel: EmptyShadeViewModel,
+    ): Unit = coroutineScope {
+        ModesEmptyShadeFix.assertInNewMode()
+        launch {
+            emptyShadeView.repeatWhenAttachedToWindow {
+                EmptyShadeViewBinder.bind(
+                    emptyShadeView,
+                    emptyShadeViewModel,
+                    notificationActivityStarter.get(),
                 )
             }
+        }
+        launch {
+            viewModel.shouldShowEmptyShadeViewAnimated.collect { shouldShow ->
+                emptyShadeView.setVisible(shouldShow.value, shouldShow.isAnimating) {
+                    shouldShow.stopAnimating()
+                }
+            }
+        }
     }
 
     private suspend fun bindSilentHeaderClickListener(
@@ -261,7 +314,7 @@
     private fun clearSilentNotifications(
         view: NotificationStackScrollLayout,
         closeShade: Boolean,
-        hideSilentSection: Boolean
+        hideSilentSection: Boolean,
     ) {
         view.clearSilentNotifications(closeShade, hideSilentSection)
     }
@@ -270,11 +323,7 @@
         if (NotificationsLiveDataStoreRefactor.isEnabled) {
             viewModel.logger.getOrNull()?.let { viewModel ->
                 loggerOptional.getOrNull()?.let { logger ->
-                    NotificationStatsLoggerBinder.bindLogger(
-                        view,
-                        logger,
-                        viewModel,
-                    )
+                    NotificationStatsLoggerBinder.bindLogger(view, logger, viewModel)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 2e37dea..87d70ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -88,6 +88,14 @@
                 viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) }
             }
             launch { viewModel.qsExpandFraction.collect { view.setQsExpandFraction(it) } }
+            launch {
+                viewModel.isShowingStackOnLockscreen.collect {
+                    view.setShowingStackOnLockscreen(it)
+                }
+            }
+            launch {
+                viewModel.alphaForLockscreenFadeIn.collect { view.setAlphaForLockscreenFadeIn(it) }
+            }
             launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
             launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
             launch {
@@ -103,6 +111,7 @@
             launch {
                 viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() }
             }
+            launch { viewModel.suppressHeightUpdates.collect { view.suppressHeightUpdates(it) } }
 
             launchAndDispose {
                 view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
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 5ae5a32..b22143f 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
@@ -16,16 +16,12 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewbinder
 
-import android.view.View
-import android.view.WindowInsets
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.common.ui.view.onApplyWindowInsets
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -39,9 +35,6 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.update
 import kotlinx.coroutines.launch
 
 /** Binds the shared notification container to its view-model. */
@@ -85,7 +78,6 @@
                 }
             }
 
-        val burnInParams = MutableStateFlow(BurnInParameters())
         val viewState = ViewStateAccessor(alpha = { controller.getAlpha() })
 
         /*
@@ -140,9 +132,7 @@
 
                     if (!SceneContainerFlag.isEnabled) {
                         launch {
-                            burnInParams
-                                .flatMapLatest { params -> viewModel.translationY(params) }
-                                .collect { y -> controller.setTranslationY(y) }
+                            viewModel.translationY.collect { y -> controller.setTranslationY(y) }
                         }
                     }
 
@@ -178,16 +168,6 @@
 
         controller.setOnHeightChangedRunnable { viewModel.notificationStackChanged() }
         disposables += DisposableHandle { controller.setOnHeightChangedRunnable(null) }
-
-        disposables +=
-            view.onApplyWindowInsets { _: View, insets: WindowInsets ->
-                val insetTypes = WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout()
-                burnInParams.update { current ->
-                    current.copy(topInset = insets.getInsetsIgnoringVisibility(insetTypes).top)
-                }
-                insets
-            }
-
         disposables += view.onLayoutChanged { viewModel.notificationStackChanged() }
 
         return disposables
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 4e2a46d..935e2a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -23,14 +23,14 @@
 import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
+import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
 import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
 import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
 import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
-import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
 import com.android.systemui.util.kotlin.FlowDumperImpl
 import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.sample
@@ -48,22 +48,24 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 
-/** ViewModel for the list of notifications. */
+/**
+ * ViewModel for the list of notifications, including child elements like the Clear all/Manage
+ * button at the bottom (the footer) and the "No notifications" text (the empty shade).
+ */
 class NotificationListViewModel
 @Inject
 constructor(
     val shelf: NotificationShelfViewModel,
     val hideListViewModel: HideListViewModel,
     val footer: Optional<FooterViewModel>,
+    val emptyShadeViewFactory: EmptyShadeViewModel.Factory,
     val logger: Optional<NotificationLoggerViewModel>,
     activeNotificationsInteractor: ActiveNotificationsInteractor,
     notificationStackInteractor: NotificationStackInteractor,
     private val headsUpNotificationInteractor: HeadsUpNotificationInteractor,
     remoteInputInteractor: RemoteInputInteractor,
-    seenNotificationsInteractor: SeenNotificationsInteractor,
     shadeInteractor: ShadeInteractor,
     userSetupInteractor: UserSetupInteractor,
-    zenModeInteractor: ZenModeInteractor,
     @Background bgDispatcher: CoroutineDispatcher,
     dumpManager: DumpManager,
 ) : FlowDumperImpl(dumpManager) {
@@ -90,6 +92,7 @@
     }
 
     val shouldShowEmptyShadeView: Flow<Boolean> by lazy {
+        ModesEmptyShadeFix.assertInLegacyMode()
         if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
             flowOf(false)
         } else {
@@ -114,6 +117,45 @@
         }
     }
 
+    val shouldShowEmptyShadeViewAnimated: Flow<AnimatedValue<Boolean>> by lazy {
+        if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
+            flowOf(AnimatedValue.NotAnimating(false))
+        } else {
+            combine(
+                    activeNotificationsInteractor.areAnyNotificationsPresent,
+                    shadeInteractor.isQsFullscreen,
+                    notificationStackInteractor.isShowingOnLockscreen,
+                ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
+                    when {
+                        hasNotifications -> false
+                        isQsFullScreen -> false
+                        // Do not show the empty shade if the lockscreen is visible (including AOD
+                        // b/228790482 and bouncer b/267060171), except if the shade is opened on
+                        // top.
+                        isShowingOnLockscreen -> false
+                        else -> true
+                    }
+                }
+                .distinctUntilChanged()
+                .sample(
+                    // TODO(b/322167853): This check is currently duplicated in FooterViewModel
+                    //  but instead it should be a field in ShadeAnimationInteractor.
+                    combine(
+                            shadeInteractor.isShadeFullyExpanded,
+                            shadeInteractor.isShadeTouchable,
+                            ::Pair,
+                        )
+                        .onStart { emit(Pair(false, false)) }
+                ) { visible, (isShadeFullyExpanded, animationsEnabled) ->
+                    val shouldAnimate = isShadeFullyExpanded && animationsEnabled
+                    AnimatableEvent(visible, shouldAnimate)
+                }
+                .toAnimatedValueFlow()
+                .dumpWhileCollecting("shouldShowEmptyShadeViewAnimated")
+                .flowOn(bgDispatcher)
+        }
+    }
+
     /**
      * Whether the footer should not be visible for the user, even if it's present in the list (as
      * per [shouldIncludeFooterView] below).
@@ -154,7 +196,7 @@
                     userSetupInteractor.isUserSetUp,
                     notificationStackInteractor.isShowingOnLockscreen,
                     shadeInteractor.isQsFullscreen,
-                    remoteInputInteractor.isRemoteInputActive
+                    remoteInputInteractor.isRemoteInputActive,
                 ) {
                     hasNotifications,
                     isUserSetUp,
@@ -193,7 +235,7 @@
                     combine(
                             shadeInteractor.isShadeFullyExpanded,
                             shadeInteractor.isShadeTouchable,
-                            ::Pair
+                            ::Pair,
                         )
                         .onStart { emit(Pair(false, false)) }
                 ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
@@ -263,7 +305,7 @@
                     combine(
                             shadeInteractor.isShadeFullyExpanded,
                             shadeInteractor.isShadeTouchable,
-                            ::Pair
+                            ::Pair,
                         )
                         .onStart { emit(Pair(false, false)) }
                 ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
@@ -283,29 +325,7 @@
     enum class VisibilityChange(val visible: Boolean, val canAnimate: Boolean) {
         DISAPPEAR_WITHOUT_ANIMATION(visible = false, canAnimate = false),
         DISAPPEAR_WITH_ANIMATION(visible = false, canAnimate = true),
-        APPEAR_WITH_ANIMATION(visible = true, canAnimate = true)
-    }
-
-    // TODO(b/308591475): This should be tracked separately by the empty shade.
-    val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(false)
-        } else {
-            zenModeInteractor.areNotificationsHiddenInShade.dumpWhileCollecting(
-                "areNotificationsHiddenInShade"
-            )
-        }
-    }
-
-    // TODO(b/308591475): This should be tracked separately by the empty shade.
-    val hasFilteredOutSeenNotifications: Flow<Boolean> by lazy {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(false)
-        } else {
-            seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpWhileCollecting(
-                "hasFilteredOutSeenNotifications"
-            )
-        }
+        APPEAR_WITH_ANIMATION(visible = true, canAnimate = true),
     }
 
     val hasClearableAlertingNotifications: Flow<Boolean> by lazy {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLockscreenScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLockscreenScrimViewModel.kt
new file mode 100644
index 0000000..84aa997
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationLockscreenScrimViewModel.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.stack.ui.viewmodel
+
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
+import com.android.systemui.util.kotlin.ActivatableFlowDumper
+import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class NotificationLockscreenScrimViewModel
+@AssistedInject
+constructor(
+    dumpManager: DumpManager,
+    shadeInteractor: ShadeInteractor,
+    private val stackAppearanceInteractor: NotificationStackAppearanceInteractor,
+) :
+    ActivatableFlowDumper by ActivatableFlowDumperImpl(dumpManager, "NotificationScrollViewModel"),
+    ExclusiveActivatable() {
+
+    val shadeMode = shadeInteractor.shadeMode
+
+    /** Sets the alpha to apply to the NSSL for fade-in on lockscreen */
+    fun setAlphaForLockscreenFadeIn(alpha: Float) {
+        stackAppearanceInteractor.setAlphaForLockscreenFadeIn(alpha)
+    }
+
+    override suspend fun onActivated(): Nothing {
+        activateFlowDumper()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): NotificationLockscreenScrimViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 5b2e02d..c9eaec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
 import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.ObservableTransitionState.Idle
 import com.android.compose.animation.scene.ObservableTransitionState.Transition
 import com.android.compose.animation.scene.ObservableTransitionState.Transition.ChangeScene
@@ -48,6 +49,7 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
 
 /** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
@@ -82,8 +84,13 @@
     private fun fullyExpandedDuringSceneChange(change: ChangeScene): Boolean {
         // The lockscreen stack is visible during all transitions away from the lockscreen, so keep
         // the stack expanded until those transitions finish.
-        return (expandedInScene(change.fromScene) && expandedInScene(change.toScene)) ||
-            change.isBetween({ it == Scenes.Lockscreen }, { true })
+        return if (change.isFrom({ it == Scenes.Lockscreen }, to = { true })) {
+            true
+        } else if (change.isFrom({ it == Scenes.Shade }, to = { it == Scenes.Lockscreen })) {
+            false
+        } else {
+            (expandedInScene(change.fromScene) && expandedInScene(change.toScene))
+        }
     }
 
     private fun expandFractionDuringSceneChange(
@@ -93,7 +100,10 @@
     ): Float {
         return if (fullyExpandedDuringSceneChange(change)) {
             1f
-        } else if (change.isBetween({ it == Scenes.Gone }, { it == Scenes.Shade })) {
+        } else if (
+            change.isBetween({ it == Scenes.Gone }, { it == Scenes.Shade }) ||
+                change.isFrom({ it == Scenes.Shade }, to = { it == Scenes.Lockscreen })
+        ) {
             shadeExpansion
         } else if (change.isBetween({ it == Scenes.Gone }, { it == Scenes.QuickSettings })) {
             // during QS expansion, increase fraction at same rate as scrim alpha,
@@ -121,6 +131,14 @@
         }
     }
 
+    /** Are notification stack height updates suppressed? */
+    val suppressHeightUpdates: Flow<Boolean> =
+        sceneInteractor.transitionState.map { transition: ObservableTransitionState ->
+            transition is Transition &&
+                transition.fromContent == Scenes.Lockscreen &&
+                (transition.toContent == Scenes.Bouncer || transition.toContent == Scenes.Gone)
+        }
+
     /**
      * The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
      * from Gone to Shade scenes, and remain at 1 when in Lockscreen or Shade scenes and while
@@ -178,6 +196,18 @@
             .distinctUntilChanged()
             .dumpWhileCollecting("shouldResetStackTop")
 
+    /** Whether the Notification Stack is visibly on the lockscreen scene. */
+    val isShowingStackOnLockscreen: Flow<Boolean> =
+        sceneInteractor.transitionState
+            .mapNotNull { state ->
+                state.isIdle(Scenes.Lockscreen) ||
+                    state.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Shade)
+            }
+            .distinctUntilChanged()
+
+    /** The alpha of the Notification Stack for lockscreen fade-in */
+    val alphaForLockscreenFadeIn = stackAppearanceInteractor.alphaForLockscreenFadeIn
+
     private operator fun SceneKey.contains(scene: SceneKey) =
         sceneInteractor.isSceneInFamily(scene, this)
 
@@ -298,3 +328,6 @@
 
 private fun ChangeScene.isBetween(a: (SceneKey) -> Boolean, b: (SceneKey) -> Boolean): Boolean =
     (a(fromScene) && b(toScene)) || (b(fromScene) && a(toScene))
+
+private fun ChangeScene.isFrom(from: (SceneKey) -> Boolean, to: (SceneKey) -> Boolean): Boolean =
+    from(fromScene) && to(toScene)
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 e34eb61..57be629 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
@@ -44,7 +44,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.keyguard.ui.viewmodel.DozingToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
@@ -112,6 +112,7 @@
     private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
     private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
+    dozingToGlanceableHubTransitionViewModel: DozingToGlanceableHubTransitionViewModel,
     private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
     private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel,
     private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
@@ -506,6 +507,7 @@
                 merge(
                         lockscreenToGlanceableHubTransitionViewModel.notificationAlpha,
                         glanceableHubToLockscreenTransitionViewModel.notificationAlpha,
+                        dozingToGlanceableHubTransitionViewModel.notificationAlpha,
                     )
                     // Manually emit on start because [notificationAlpha] only starts emitting
                     // when transitions start.
@@ -533,20 +535,18 @@
      * Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
      * translated as the keyguard fades out.
      */
-    fun translationY(params: BurnInParameters): Flow<Float> {
-        // with SceneContainer, x translation is handled by views, y is handled by compose
-        SceneContainerFlag.assertInLegacyMode()
-        return combine(
-                aodBurnInViewModel
-                    .movement(params)
-                    .map { it.translationY.toFloat() }
-                    .onStart { emit(0f) },
+    val translationY: Flow<Float> =
+        combine(
+                aodBurnInViewModel.movement.map { it.translationY.toFloat() }.onStart { emit(0f) },
                 isOnLockscreenWithoutShade,
                 merge(
                     keyguardInteractor.keyguardTranslationY,
                     occludedToLockscreenTransitionViewModel.lockscreenTranslationY,
                 ),
             ) { burnInY, isOnLockscreenWithoutShade, translationY ->
+                // with SceneContainer, x translation is handled by views, y is handled by compose
+                SceneContainerFlag.assertInLegacyMode()
+
                 if (isOnLockscreenWithoutShade) {
                     burnInY + translationY
                 } else {
@@ -554,7 +554,6 @@
                 }
             }
             .dumpWhileCollecting("translationY")
-    }
 
     /** Horizontal translation to apply to the container. */
     val translationX: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 59533b3..1d3f0e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -29,7 +29,6 @@
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
 import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
 import static com.android.systemui.Flags.lightRevealMigration;
-import static com.android.systemui.Flags.newAodTransition;
 import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
 import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
 import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
@@ -206,6 +205,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
 import com.android.systemui.statusbar.data.model.StatusBarMode;
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -1204,7 +1204,10 @@
                     setBouncerShowingForStatusBarComponents(mBouncerShowing);
                     checkBarModes();
                 });
-        mStatusBarInitializer.initializeStatusBar();
+        // When the flag is on, we register the fragment as a core startable and this is not needed
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            mStatusBarInitializer.initializeStatusBar();
+        }
 
         mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView());
 
@@ -1509,7 +1512,6 @@
         mNotificationShadeWindowController.fetchWindowRootView();
         getNotificationShadeWindowViewController().setupExpandedStatusBar();
         getNotificationShadeWindowViewController().setupCommunalHubLayout();
-        mBackActionInteractor.setup(mQsController, mShadeSurface);
     }
 
     protected NotificationShadeWindowViewController getNotificationShadeWindowViewController() {
@@ -2547,7 +2549,7 @@
             mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
                 mDeviceInteractive = true;
 
-                boolean isFlaggedOff = newAodTransition() && MigrateClocksToBlueprint.isEnabled();
+                boolean isFlaggedOff = MigrateClocksToBlueprint.isEnabled();
                 if (!isFlaggedOff && shouldAnimateDozeWakeup()) {
                     // If this is false, the power button must be physically pressed in order to
                     // trigger fingerprint authentication.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 0a6e7f5..ee96195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -43,6 +43,7 @@
 import android.util.EventLog;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.jank.InteractionJankMonitor;
@@ -74,6 +75,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
@@ -249,7 +251,7 @@
      * Called when a notification is clicked.
      *
      * @param entry notification that was clicked
-     * @param row row for that notification
+     * @param row   row for that notification
      */
     @Override
     public void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row) {
@@ -547,8 +549,8 @@
                             (adapter) -> TaskStackBuilder.create(mContext)
                                     .addNextIntentWithParentStack(intent)
                                     .startActivities(getActivityOptions(
-                                            mDisplayId,
-                                            adapter),
+                                                    mDisplayId,
+                                                    adapter),
                                             new UserHandle(UserHandle.getUserId(appUid))));
                 });
                 return true;
@@ -565,6 +567,7 @@
 
     @Override
     public void startHistoryIntent(View view, boolean showHistory) {
+        ModesEmptyShadeFix.assertInLegacyMode();
         boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */);
         ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
             @Override
@@ -585,14 +588,14 @@
                             );
                     ActivityTransitionAnimator.Controller animationController =
                             viewController == null ? null
-                                : new StatusBarTransitionAnimatorController(
-                                        viewController,
-                                        mShadeAnimationInteractor,
-                                        mShadeController,
-                                        mNotificationShadeWindowController,
-                                        mCommandQueue,
-                                        mDisplayId,
-                                        true /* isActivityIntent */);
+                                    : new StatusBarTransitionAnimatorController(
+                                            viewController,
+                                            mShadeAnimationInteractor,
+                                            mShadeController,
+                                            mNotificationShadeWindowController,
+                                            mCommandQueue,
+                                            mDisplayId,
+                                            true /* isActivityIntent */);
 
                     mActivityTransitionAnimator.startIntentWithAnimation(
                             animationController, animate, intent.getPackage(),
@@ -612,6 +615,51 @@
                 false /* afterKeyguardGone */);
     }
 
+    @Override
+    public void startSettingsIntent(@NonNull View view, @NonNull SettingsIntent intentInfo) {
+        boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */);
+        ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
+            @Override
+            public boolean onDismiss() {
+                AsyncTask.execute(() -> {
+                    TaskStackBuilder tsb = TaskStackBuilder.create(mContext);
+                    for (Intent intent : intentInfo.getBackStack()) {
+                        tsb.addNextIntent(intent);
+                    }
+                    tsb.addNextIntent(intentInfo.getTargetIntent());
+
+                    ActivityTransitionAnimator.Controller viewController =
+                            ActivityTransitionAnimator.Controller.fromView(view,
+                                    intentInfo.getCujType());
+                    ActivityTransitionAnimator.Controller animationController =
+                            viewController == null ? null
+                                    : new StatusBarTransitionAnimatorController(
+                                            viewController,
+                                            mShadeAnimationInteractor,
+                                            mShadeController,
+                                            mNotificationShadeWindowController,
+                                            mCommandQueue,
+                                            mDisplayId,
+                                            true /* isActivityIntent */);
+
+                    mActivityTransitionAnimator.startIntentWithAnimation(
+                            animationController, animate, intentInfo.getTargetIntent().getPackage(),
+                            (adapter) -> tsb.startActivities(
+                                    getActivityOptions(mDisplayId, adapter),
+                                    mUserTracker.getUserHandle()));
+                });
+                return true;
+            }
+
+            @Override
+            public boolean willRunAnimationOnKeyguard() {
+                return animate;
+            }
+        };
+        mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
+                false /* afterKeyguardGone */);
+    }
+
     private void removeHunAfterClick(ExpandableNotificationRow row) {
         String key = row.getEntry().getSbn().getKey();
         if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUpEntry(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
deleted file mode 100644
index 942d186..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2020 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.phone.dagger;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
-
-import dagger.Binds;
-import dagger.Module;
-
-/**
- * Dagger Module providing {@link CentralSurfacesImpl}.
- */
-@Module
-public interface StatusBarPhoneModule {
-    /**
-     * Provides our instance of CentralSurfaces which is considered optional.
-     */
-    @Binds
-    @SysUISingleton
-    CentralSurfaces bindsCentralSurfaces(CentralSurfacesImpl impl);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
new file mode 100644
index 0000000..13b651e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 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.phone.dagger
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.core.StatusBarInitializer
+import com.android.systemui.statusbar.core.StatusBarInitializerImpl
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+/** Similar in purpose to [StatusBarModule], but scoped only to phones */
+@Module
+interface StatusBarPhoneModule {
+
+    /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
+    @Binds
+    @IntoMap
+    @ClassKey(StatusBarInitializerImpl::class)
+    fun bindStatusBarInitializer(impl: StatusBarInitializerImpl): CoreStartable
+
+    @Binds fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 659cee3..a8b4728 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -55,8 +55,9 @@
 import com.android.systemui.statusbar.OperatorNameView;
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.chips.shared.StatusBarRonChips;
-import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips;
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -366,8 +367,10 @@
         mPrimaryOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip_primary);
         mSecondaryOngoingActivityChip =
                 mStatusBar.findViewById(R.id.ongoing_activity_chip_secondary);
-        showEndSideContent(false);
-        showClock(false);
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            showEndSideContent(false);
+            showClock(false);
+        }
         initOperatorName();
         initNotificationIconArea();
         mSystemEventAnimator = getSystemEventAnimator();
@@ -455,7 +458,9 @@
         super.onPause();
         mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
-        mOngoingCallController.removeCallback(mOngoingCallListener);
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            mOngoingCallController.removeCallback(mOngoingCallListener);
+        }
         mAnimationScheduler.removeCallback(this);
         mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver);
     }
@@ -490,7 +495,9 @@
         mNotificationIconAreaInner = notificationIcons;
         mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
 
-        updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
+        }
         Trace.endSection();
     }
 
@@ -509,11 +516,17 @@
             new StatusBarVisibilityChangeListener() {
                 @Override
                 public void onStatusBarVisibilityMaybeChanged() {
+                    if (StatusBarSimpleFragment.isEnabled()) {
+                        return;
+                    }
                     updateStatusBarVisibilities(/* animate= */ true);
                 }
 
                 @Override
                 public void onTransitionFromLockscreenToDreamStarted() {
+                    if (StatusBarSimpleFragment.isEnabled()) {
+                        return;
+                    }
                     mTransitionFromLockscreenToDreamStarted = true;
                 }
 
@@ -522,6 +535,9 @@
                         boolean hasPrimaryOngoingActivity,
                         boolean hasSecondaryOngoingActivity,
                         boolean shouldAnimate) {
+                    if (StatusBarSimpleFragment.isEnabled()) {
+                        return;
+                    }
                     mHasPrimaryOngoingActivity = hasPrimaryOngoingActivity;
                     mHasSecondaryOngoingActivity = hasSecondaryOngoingActivity;
                     updateStatusBarVisibilities(shouldAnimate);
@@ -530,6 +546,9 @@
                 @Override
                 public void onIsHomeStatusBarAllowedBySceneChanged(
                         boolean isHomeStatusBarAllowedByScene) {
+                    if (StatusBarSimpleFragment.isEnabled()) {
+                        return;
+                    }
                     mHomeStatusBarAllowedByScene = isHomeStatusBarAllowedByScene;
                     updateStatusBarVisibilities(/* animate= */ true);
                 }
@@ -537,17 +556,22 @@
 
     @Override
     public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (StatusBarSimpleFragment.isEnabled()) {
+            return;
+        }
         if (displayId != getContext().getDisplayId()) {
             return;
         }
         mCollapsedStatusBarFragmentLogger
-                .logDisableFlagChange(new DisableState(state1, state2));
+                .logDisableFlagChange(new DisableFlagsLogger.DisableState(state1, state2));
         mLastSystemVisibility =
                 StatusBarVisibilityModel.createModelFromFlags(state1, state2);
         updateStatusBarVisibilities(animate);
     }
 
     private void updateStatusBarVisibilities(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
+
         StatusBarVisibilityModel previousModel = mLastModifiedVisibility;
         StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility);
         mCollapsedStatusBarFragmentLogger.logVisibilityModel(newModel);
@@ -587,6 +611,8 @@
 
     private StatusBarVisibilityModel calculateInternalModel(
             StatusBarVisibilityModel externalModel) {
+        StatusBarSimpleFragment.assertInLegacyMode();
+
         // TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead.
         boolean headsUpVisible =
                 mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
@@ -639,6 +665,8 @@
      * mLastModifiedVisibility.
      */
     private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
+
         StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility;
         boolean disableNotifications = !visibilityModel.getShowNotificationIcons();
         boolean hasOngoingActivity = visibilityModel.getShowPrimaryOngoingActivityChip();
@@ -674,6 +702,8 @@
     }
 
     private boolean shouldHideStatusBar() {
+        StatusBarSimpleFragment.assertInLegacyMode();
+
         if (!mShadeExpansionStateManager.isClosed()
                 && mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded()) {
             return true;
@@ -728,6 +758,7 @@
     }
 
     private void hideEndSideContent(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         if (!animate || !mAnimationsEnabled) {
             mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER);
         } else {
@@ -737,6 +768,7 @@
     }
 
     private void showEndSideContent(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         if (!animate || !mAnimationsEnabled) {
             mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER);
             return;
@@ -753,15 +785,18 @@
     }
 
     private void hideClock(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateHiddenState(mClockView, clockHiddenMode(), animate);
     }
 
     private void showClock(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateShow(mClockView, animate);
     }
 
     /** Hides the primary ongoing activity chip. */
     private void hidePrimaryOngoingActivityChip(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateHiddenState(mPrimaryOngoingActivityChip, View.GONE, animate);
     }
 
@@ -773,15 +808,18 @@
      * activities. See b/332662551.
      */
     private void showPrimaryOngoingActivityChip(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateShow(mPrimaryOngoingActivityChip, animate);
     }
 
     private void hideSecondaryOngoingActivityChip(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateHiddenState(mSecondaryOngoingActivityChip, View.GONE, animate);
     }
 
     private void showSecondaryOngoingActivityChip(boolean animate) {
         StatusBarRonChips.assertInNewMode();
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateShow(mSecondaryOngoingActivityChip, animate);
     }
 
@@ -790,6 +828,7 @@
      * don't set the clock GONE otherwise it'll mess up the animation.
      */
     private int clockHiddenMode() {
+        StatusBarSimpleFragment.assertInLegacyMode();
         if (!mShadeExpansionStateManager.isClosed() && !mKeyguardStateController.isShowing()
                 && !mStatusBarStateController.isDozing()) {
             return View.INVISIBLE;
@@ -798,20 +837,24 @@
     }
 
     public void hideNotificationIconArea(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateHide(mNotificationIconAreaInner, animate);
     }
 
     public void showNotificationIconArea(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateShow(mNotificationIconAreaInner, animate);
     }
 
     public void hideOperatorName(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         if (mOperatorNameViewController != null) {
             animateHide(mOperatorNameViewController.getView(), animate);
         }
     }
 
     public void showOperatorName(boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         if (mOperatorNameViewController != null) {
             animateShow(mOperatorNameViewController.getView(), animate);
         }
@@ -821,6 +864,7 @@
      * Animate a view to INVISIBLE or GONE
      */
     private void animateHiddenState(final View v, int state, boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         v.animate().cancel();
         if (!animate || !mAnimationsEnabled) {
             v.setAlpha(0f);
@@ -840,6 +884,7 @@
      * Hides a view.
      */
     private void animateHide(final View v, boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         animateHiddenState(v, View.INVISIBLE, animate);
     }
 
@@ -847,6 +892,7 @@
      * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable.
      */
     private void animateShow(View v, boolean animate) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         v.animate().cancel();
         v.setVisibility(View.VISIBLE);
         if (!animate || !mAnimationsEnabled) {
@@ -883,13 +929,17 @@
             mOperatorNameViewController.init();
             // This view should not be visible on lock-screen
             if (mKeyguardStateController.isShowing()) {
-                hideOperatorName(false);
+                if (!StatusBarSimpleFragment.isEnabled()) {
+                    hideOperatorName(false);
+                }
             }
         }
     }
 
     private void initOngoingCallChip() {
-        mOngoingCallController.addCallback(mOngoingCallListener);
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            mOngoingCallController.addCallback(mOngoingCallListener);
+        }
         // TODO(b/364653005): Do we also need to set the secondary activity chip?
         mOngoingCallController.setChipView(mPrimaryOngoingActivityChip);
     }
@@ -899,6 +949,9 @@
 
     @Override
     public void onDozingChanged(boolean isDozing) {
+        if (StatusBarSimpleFragment.isEnabled()) {
+            return;
+        }
         updateStatusBarVisibilities(/* animate= */ false);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
index c8836e4..eaf15a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt
@@ -20,6 +20,7 @@
 import androidx.core.animation.Interpolator
 import androidx.core.animation.ValueAnimator
 import com.android.app.animation.InterpolatorsAndroidX
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment
 
 /**
  * A controller that keeps track of multiple sources applying alpha value changes to a view. It will
@@ -48,7 +49,7 @@
         sourceId: Int,
         duration: Long,
         interpolator: Interpolator = InterpolatorsAndroidX.ALPHA_IN,
-        startDelay: Long = 0
+        startDelay: Long = 0,
     ) {
         animators[sourceId]?.cancel()
         val animator = ValueAnimator.ofFloat(getMinAlpha(), alpha)
@@ -74,8 +75,10 @@
 
     private fun applyAlphaToView() {
         val minAlpha = getMinAlpha()
-        view.visibility = if (minAlpha != 0f) View.VISIBLE else View.INVISIBLE
-        view.alpha = minAlpha
+        if (!StatusBarSimpleFragment.isEnabled) {
+            view.visibility = if (minAlpha != 0f) View.VISIBLE else View.INVISIBLE
+            view.alpha = minAlpha
+        }
     }
 
     private fun getMinAlpha() = alphas.minOfOrNull { it.value } ?: initialAlpha
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 68163b2..4ef328c 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
@@ -149,7 +149,7 @@
     override val isForceHidden: Flow<Boolean>,
     connectionRepository: MobileConnectionRepository,
     private val context: Context,
-    val carrierIdOverrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
+    val carrierIdOverrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl(),
 ) : MobileIconInteractor {
     override val tableLogBuffer: TableLogBuffer = connectionRepository.tableLogBuffer
 
@@ -182,7 +182,7 @@
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
-                connectionRepository.networkName.value
+                connectionRepository.networkName.value,
             )
 
     override val carrierName =
@@ -198,7 +198,7 @@
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
-                connectionRepository.carrierName.value.name
+                connectionRepository.carrierName.value.name,
             )
 
     /** What the mobile icon would be before carrierId overrides */
@@ -219,10 +219,7 @@
             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
 
     override val networkTypeIconGroup =
-        combine(
-                defaultNetworkType,
-                carrierIdIconOverrideExists,
-            ) { networkType, overrideExists ->
+        combine(defaultNetworkType, carrierIdIconOverrideExists) { networkType, overrideExists ->
                 // DefaultIcon comes out of the icongroup lookup, we check for overrides here
                 if (overrideExists) {
                     val iconOverride =
@@ -316,30 +313,30 @@
 
     /** Whether or not to show the error state of [SignalDrawable] */
     private val showExclamationMark: StateFlow<Boolean> =
-        combine(
-                defaultSubscriptionHasDataEnabled,
+        combine(defaultSubscriptionHasDataEnabled, isDefaultConnectionFailed, isInService) {
+                isDefaultDataEnabled,
                 isDefaultConnectionFailed,
-                isInService,
-            ) { isDefaultDataEnabled, isDefaultConnectionFailed, isInService ->
+                isInService ->
                 !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), true)
 
     private val cellularShownLevel: StateFlow<Int> =
-        combine(
+        combine(level, isInService, connectionRepository.inflateSignalStrength) {
                 level,
                 isInService,
-                connectionRepository.inflateSignalStrength,
-            ) { level, isInService, inflate ->
+                inflate ->
                 if (isInService) {
                     if (inflate) level + 1 else level
                 } else 0
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
 
-    // Satellite level is unaffected by the isInService or inflateSignalStrength properties
+    // Satellite level is unaffected by the inflateSignalStrength property
     // See b/346904529 for details
-    private val satelliteShownLevel: StateFlow<Int> = level
+    private val satelliteShownLevel: StateFlow<Int> =
+        combine(level, isInService) { level, isInService -> if (isInService) level else 0 }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
 
     private val cellularIcon: Flow<SignalIconModel.Cellular> =
         combine(
@@ -362,7 +359,7 @@
                 level = it,
                 icon =
                     SatelliteIconModel.fromSignalStrength(it)
-                        ?: SatelliteIconModel.fromSignalStrength(0)!!
+                        ?: SatelliteIconModel.fromSignalStrength(0)!!,
             )
         }
 
@@ -383,11 +380,7 @@
                 }
             }
             .distinctUntilChanged()
-            .logDiffsForTable(
-                tableLogBuffer,
-                columnPrefix = "icon",
-                initialValue = initial,
-            )
+            .logDiffsForTable(tableLogBuffer, columnPrefix = "icon", initialValue = initial)
             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
     }
 }
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 deae576..bad6f80 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -29,6 +30,7 @@
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancel
@@ -114,7 +116,7 @@
 
     private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> {
         // Create a child scope so we can cancel it
-        val vmScope = scope.createChildScope()
+        val vmScope = scope.createChildScope(createCoroutineTracingContext("MobileIconViewModel"))
         val vm =
             MobileIconViewModel(
                 subId,
@@ -128,8 +130,8 @@
         return Pair(vm, vmScope)
     }
 
-    private fun CoroutineScope.createChildScope() =
-        CoroutineScope(coroutineContext + Job(coroutineContext[Job]))
+    private fun CoroutineScope.createChildScope(extraContext: CoroutineContext) =
+        CoroutineScope(coroutineContext + Job(coroutineContext[Job]) + extraContext)
 
     private fun invalidateCaches(subIds: List<Int>) {
         reuseCache.keys
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt
new file mode 100644
index 0000000..9164da7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.pipeline.shared.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.pipeline.shared.domain.model.StatusBarDisableFlagsVisibilityModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Interactor for the home screen status bar (aka
+ * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment]).
+ */
+@SysUISingleton
+class CollapsedStatusBarInteractor
+@Inject
+constructor(disableFlagsRepository: DisableFlagsRepository) {
+    /**
+     * The visibilities of various status bar child views, based only on the information we received
+     * from disable flags.
+     */
+    val visibilityViaDisableFlags: Flow<StatusBarDisableFlagsVisibilityModel> =
+        disableFlagsRepository.disableFlags.map {
+            StatusBarDisableFlagsVisibilityModel(
+                isClockAllowed = it.isClockEnabled,
+                areNotificationIconsAllowed = it.areNotificationIconsEnabled,
+                isSystemInfoAllowed = it.isSystemInfoEnabled,
+                animate = it.animate,
+            )
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/model/StatusBarDisableFlagsVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/model/StatusBarDisableFlagsVisibilityModel.kt
new file mode 100644
index 0000000..69e9746
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/model/StatusBarDisableFlagsVisibilityModel.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.pipeline.shared.domain.model
+
+/**
+ * Represents the visibilities of various status bar child views, based only on the information we
+ * received from disable flags.
+ */
+data class StatusBarDisableFlagsVisibilityModel(
+    /** True if the clock is allowed to be shown. */
+    val isClockAllowed: Boolean,
+    /** True if the notification icons are allowed to be shown. */
+    val areNotificationIconsAllowed: Boolean,
+    /** True if the system information (wifi, mobile, etc.) is allowed to be shown. */
+    val isSystemInfoAllowed: Boolean,
+    /** True if we should animate any view visibility changes and false otherwise. */
+    val animate: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 49eabba..eea4c21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -21,6 +21,7 @@
 import android.view.View
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
 import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -28,7 +29,9 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.launch
@@ -86,19 +89,30 @@
                     launch {
                         viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
                             OngoingActivityChipBinder.bind(primaryChipModel, primaryChipView)
-                            when (primaryChipModel) {
-                                is OngoingActivityChipModel.Shown ->
-                                    listener.onOngoingActivityStatusChanged(
-                                        hasPrimaryOngoingActivity = true,
-                                        hasSecondaryOngoingActivity = false,
-                                        shouldAnimate = true,
-                                    )
-                                is OngoingActivityChipModel.Hidden ->
-                                    listener.onOngoingActivityStatusChanged(
-                                        hasPrimaryOngoingActivity = false,
-                                        hasSecondaryOngoingActivity = false,
-                                        shouldAnimate = primaryChipModel.shouldAnimate,
-                                    )
+                            if (StatusBarSimpleFragment.isEnabled) {
+                                when (primaryChipModel) {
+                                    is OngoingActivityChipModel.Shown ->
+                                        primaryChipView.show(shouldAnimateChange = true)
+                                    is OngoingActivityChipModel.Hidden ->
+                                        primaryChipView.hide(
+                                            shouldAnimateChange = primaryChipModel.shouldAnimate
+                                        )
+                                }
+                            } else {
+                                when (primaryChipModel) {
+                                    is OngoingActivityChipModel.Shown ->
+                                        listener.onOngoingActivityStatusChanged(
+                                            hasPrimaryOngoingActivity = true,
+                                            hasSecondaryOngoingActivity = false,
+                                            shouldAnimate = true,
+                                        )
+                                    is OngoingActivityChipModel.Hidden ->
+                                        listener.onOngoingActivityStatusChanged(
+                                            hasPrimaryOngoingActivity = false,
+                                            hasSecondaryOngoingActivity = false,
+                                            shouldAnimate = primaryChipModel.shouldAnimate,
+                                        )
+                                }
                             }
                         }
                     }
@@ -115,14 +129,22 @@
                             // TODO(b/364653005): Don't show the secondary chip if there isn't
                             // enough space for it.
                             OngoingActivityChipBinder.bind(chips.secondary, secondaryChipView)
-                            listener.onOngoingActivityStatusChanged(
-                                hasPrimaryOngoingActivity =
-                                    chips.primary is OngoingActivityChipModel.Shown,
-                                hasSecondaryOngoingActivity =
-                                    chips.secondary is OngoingActivityChipModel.Shown,
-                                // TODO(b/364653005): Figure out the animation story here.
-                                shouldAnimate = true,
-                            )
+
+                            if (StatusBarSimpleFragment.isEnabled) {
+                                primaryChipView.adjustVisibility(chips.primary.toVisibilityModel())
+                                secondaryChipView.adjustVisibility(
+                                    chips.secondary.toVisibilityModel()
+                                )
+                            } else {
+                                listener.onOngoingActivityStatusChanged(
+                                    hasPrimaryOngoingActivity =
+                                        chips.primary is OngoingActivityChipModel.Shown,
+                                    hasSecondaryOngoingActivity =
+                                        chips.secondary is OngoingActivityChipModel.Shown,
+                                    // TODO(b/364653005): Figure out the animation story here.
+                                    shouldAnimate = true,
+                                )
+                            }
                         }
                     }
                 }
@@ -134,10 +156,42 @@
                         }
                     }
                 }
+
+                if (StatusBarSimpleFragment.isEnabled) {
+                    val clockView = view.requireViewById<View>(R.id.clock)
+                    launch { viewModel.isClockVisible.collect { clockView.adjustVisibility(it) } }
+
+                    val notificationIconsArea = view.requireViewById<View>(R.id.notificationIcons)
+                    launch {
+                        viewModel.isNotificationIconContainerVisible.collect {
+                            notificationIconsArea.adjustVisibility(it)
+                        }
+                    }
+
+                    val systemInfoView =
+                        view.requireViewById<View>(R.id.status_bar_end_side_content)
+                    // TODO(b/364360986): Also handle operator name view.
+                    launch {
+                        viewModel.isSystemInfoVisible.collect {
+                            systemInfoView.adjustVisibility(it)
+                            // TODO(b/364360986): The system info view has a custom alpha controller
+                            // in CollapsedStatusBarFragment.
+                        }
+                    }
+                }
             }
         }
     }
 
+    private fun OngoingActivityChipModel.toVisibilityModel():
+        CollapsedStatusBarViewModel.VisibilityModel {
+        return CollapsedStatusBarViewModel.VisibilityModel(
+            visibility = if (this is OngoingActivityChipModel.Shown) View.VISIBLE else View.GONE,
+            // TODO(b/364653005): Figure out the animation story here.
+            shouldAnimateChange = true,
+        )
+    }
+
     private fun animateLightsOutView(view: View, visible: Boolean) {
         view.animate().cancel()
 
@@ -167,6 +221,54 @@
             )
             .start()
     }
+
+    private fun View.adjustVisibility(model: CollapsedStatusBarViewModel.VisibilityModel) {
+        if (model.visibility == View.VISIBLE) {
+            this.show(model.shouldAnimateChange)
+        } else {
+            this.hide(model.visibility, model.shouldAnimateChange)
+        }
+    }
+
+    // See CollapsedStatusBarFragment#hide.
+    private fun View.hide(state: Int = View.INVISIBLE, shouldAnimateChange: Boolean) {
+        val v = this
+        v.animate().cancel()
+        if (!shouldAnimateChange) {
+            v.alpha = 0f
+            v.visibility = state
+            return
+        }
+
+        v.animate()
+            .alpha(0f)
+            .setDuration(CollapsedStatusBarFragment.FADE_OUT_DURATION.toLong())
+            .setStartDelay(0)
+            .setInterpolator(Interpolators.ALPHA_OUT)
+            .withEndAction { v.visibility = state }
+    }
+
+    // See CollapsedStatusBarFragment#show.
+    private fun View.show(shouldAnimateChange: Boolean) {
+        val v = this
+        v.animate().cancel()
+        v.visibility = View.VISIBLE
+        if (!shouldAnimateChange) {
+            v.alpha = 1f
+            return
+        }
+        v.animate()
+            .alpha(1f)
+            .setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION.toLong())
+            .setInterpolator(Interpolators.ALPHA_IN)
+            .setStartDelay(CollapsedStatusBarFragment.FADE_IN_DELAY.toLong())
+            // We need to clean up any pending end action from animateHide if we call both hide and
+            // show in the same frame before the animation actually gets started.
+            // cancel() doesn't really remove the end action.
+            .withEndAction(null)
+
+        // TODO(b/364360986): Synchronize the motion with the Keyguard fading if necessary.
+    }
 }
 
 /** Listener for various events that may affect the status bar's visibility. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileIconModel.kt
new file mode 100644
index 0000000..f8958e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileIconModel.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.pipeline.shared.ui.model
+
+import com.android.systemui.common.shared.model.Icon
+
+sealed interface InternetTileIconModel {
+    data class ResourceId(val resId: Int) : InternetTileIconModel
+
+    data class Cellular(val level: Int) : InternetTileIconModel
+
+    data class Satellite(val resourceIcon: Icon.Resource) : InternetTileIconModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index 9cce2b8..366ea35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -16,23 +16,29 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import android.view.View
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 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.GONE
 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.TransitionState
 import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
+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.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
+import com.android.systemui.statusbar.pipeline.shared.domain.interactor.CollapsedStatusBarInteractor
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel.VisibilityModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -80,9 +86,16 @@
     /**
      * True if the current scene can show the home status bar (aka this status bar), and false if
      * the current scene should never show the home status bar.
+     *
+     * TODO(b/364360986): Once the is<SomeChildView>Visible flows are fully enabled, we shouldn't
+     *   need this flow anymore.
      */
     val isHomeStatusBarAllowedByScene: StateFlow<Boolean>
 
+    val isClockVisible: Flow<VisibilityModel>
+    val isNotificationIconContainerVisible: Flow<VisibilityModel>
+    val isSystemInfoVisible: Flow<VisibilityModel>
+
     /**
      * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where
      * status bar and navigation icons dim. In this mode, a notification dot appears where the
@@ -93,17 +106,26 @@
      * [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE].
      */
     fun areNotificationsLightsOut(displayId: Int): Flow<Boolean>
+
+    /** Models the current visibility for a specific child view of status bar. */
+    data class VisibilityModel(
+        @View.Visibility val visibility: Int,
+        /** True if a visibility change should be animated. */
+        val shouldAnimateChange: Boolean,
+    )
 }
 
 @SysUISingleton
 class CollapsedStatusBarViewModelImpl
 @Inject
 constructor(
+    collapsedStatusBarInteractor: CollapsedStatusBarInteractor,
     private val lightsOutInteractor: LightsOutInteractor,
     private val notificationsInteractor: ActiveNotificationsInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     sceneInteractor: SceneInteractor,
     sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
+    shadeInteractor: ShadeInteractor,
     ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
     @Application coroutineScope: CoroutineScope,
 ) : CollapsedStatusBarViewModel {
@@ -148,4 +170,59 @@
                 }
                 .distinctUntilChanged()
         }
+
+    /**
+     * True if the current SysUI state can show the home status bar (aka this status bar), and false
+     * if we shouldn't be showing any part of the home status bar.
+     */
+    private val isHomeScreenStatusBarAllowedLegacy: Flow<Boolean> =
+        combine(
+            keyguardTransitionInteractor.currentKeyguardState,
+            shadeInteractor.isAnyFullyExpanded,
+        ) { currentKeyguardState, isShadeExpanded ->
+            (currentKeyguardState == GONE || currentKeyguardState == OCCLUDED) && !isShadeExpanded
+            // TODO(b/364360986): Add edge cases, like secure camera launch.
+        }
+
+    private val isHomeScreenStatusBarAllowed: Flow<Boolean> =
+        if (SceneContainerFlag.isEnabled) {
+            isHomeStatusBarAllowedByScene
+        } else {
+            isHomeScreenStatusBarAllowedLegacy
+        }
+
+    override val isClockVisible: Flow<VisibilityModel> =
+        combine(
+            isHomeScreenStatusBarAllowed,
+            collapsedStatusBarInteractor.visibilityViaDisableFlags,
+        ) { isStatusBarAllowed, visibilityViaDisableFlags ->
+            val showClock = isStatusBarAllowed && visibilityViaDisableFlags.isClockAllowed
+            // TODO(b/364360986): Take CollapsedStatusBarFragment.clockHiddenMode into account.
+            VisibilityModel(showClock.toVisibilityInt(), visibilityViaDisableFlags.animate)
+        }
+    override val isNotificationIconContainerVisible: Flow<VisibilityModel> =
+        combine(
+            isHomeScreenStatusBarAllowed,
+            collapsedStatusBarInteractor.visibilityViaDisableFlags,
+        ) { isStatusBarAllowed, visibilityViaDisableFlags ->
+            val showNotificationIconContainer =
+                isStatusBarAllowed && visibilityViaDisableFlags.areNotificationIconsAllowed
+            VisibilityModel(
+                showNotificationIconContainer.toVisibilityInt(),
+                visibilityViaDisableFlags.animate,
+            )
+        }
+    override val isSystemInfoVisible: Flow<VisibilityModel> =
+        combine(
+            isHomeScreenStatusBarAllowed,
+            collapsedStatusBarInteractor.visibilityViaDisableFlags,
+        ) { isStatusBarAllowed, visibilityViaDisableFlags ->
+            val showSystemInfo = isStatusBarAllowed && visibilityViaDisableFlags.isSystemInfoAllowed
+            VisibilityModel(showSystemInfo.toVisibilityInt(), visibilityViaDisableFlags.animate)
+        }
+
+    @View.Visibility
+    private fun Boolean.toVisibilityInt(): Int {
+        return if (this) View.VISIBLE else View.GONE
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 5ba5c06..674cbb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -196,7 +196,7 @@
             runnable.run()
             return
         }
-        var outcome = ""
+        val outcome: String
         if (entry in nextMap) {
             outcome = "remove from next"
             if (entry in nextMap) nextMap.remove(entry)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
index ba45942..daba109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -20,6 +20,7 @@
 import android.provider.Settings
 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST
 import android.util.Log
 import androidx.concurrent.futures.await
 import com.android.settingslib.notification.data.repository.ZenModeRepository
@@ -29,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.modes.shared.ModesUi
 import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
 import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
@@ -39,6 +41,7 @@
 import kotlinx.coroutines.flow.Flow
 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
 
@@ -54,8 +57,8 @@
     private val notificationSettingsRepository: NotificationSettingsRepository,
     @Background private val bgDispatcher: CoroutineDispatcher,
     private val iconLoader: ZenIconLoader,
-    private val deviceProvisioningRepository: DeviceProvisioningRepository,
-    private val userSetupRepository: UserSetupRepository,
+    deviceProvisioningRepository: DeviceProvisioningRepository,
+    userSetupRepository: UserSetupRepository,
 ) {
     val isZenAvailable: Flow<Boolean> =
         combine(
@@ -126,6 +129,25 @@
     val mainActiveMode: Flow<ZenModeInfo?> =
         activeModes.map { a -> a.mainMode }.distinctUntilChanged()
 
+    val modesHidingNotifications: Flow<List<ZenMode>> by lazy {
+        if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode() || !ModesUi.isEnabled) {
+            flowOf(listOf())
+        } else {
+            modes
+                .map { modes ->
+                    modes.filter { mode ->
+                        mode.isActive &&
+                            !mode.policy.isVisualEffectAllowed(
+                                /* effect = */ VISUAL_EFFECT_NOTIFICATION_LIST,
+                                /* defaultVal = */ true,
+                            )
+                    }
+                }
+                .flowOn(bgDispatcher)
+                .distinctUntilChanged()
+        }
+    }
+
     suspend fun getModeIcon(mode: ZenMode): ZenIcon {
         return iconLoader.getIcon(context, mode).await()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
index e1dcc52..78edd39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
@@ -19,8 +19,10 @@
 import android.content.Intent
 import android.provider.Settings
 import android.util.Log
+import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.stringResource
@@ -30,6 +32,7 @@
 import androidx.lifecycle.LifecycleOwner
 import com.android.compose.PlatformButton
 import com.android.compose.PlatformOutlinedButton
+import com.android.compose.theme.PlatformTheme
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.animation.DialogCuj
@@ -74,7 +77,7 @@
             currentDialog?.dismiss()
         }
 
-        currentDialog = sysuiDialogFactory.create() { ModesDialogContent(it) }
+        currentDialog = sysuiDialogFactory.create { ModesDialogContent(it) }
         currentDialog
             ?.lifecycle
             ?.addObserver(
@@ -91,28 +94,34 @@
 
     @Composable
     private fun ModesDialogContent(dialog: SystemUIDialog) {
-        AlertDialogContent(
-            modifier = Modifier.semantics {
-                testTagsAsResourceId = true
-            },
-            title = {
-                Text(
-                    modifier = Modifier.testTag("modes_title"),
-                    text = stringResource(R.string.zen_modes_dialog_title)
-                )
-            },
-            content = { ModeTileGrid(viewModel.get()) },
-            neutralButton = {
-                PlatformOutlinedButton(onClick = { openSettings(dialog) }) {
-                    Text(stringResource(R.string.zen_modes_dialog_settings))
-                }
-            },
-            positiveButton = {
-                PlatformButton(onClick = { dialog.dismiss() }) {
-                    Text(stringResource(R.string.zen_modes_dialog_done))
-                }
-            },
-        )
+        // TODO(b/369376884): The composable does correctly update when the theme changes
+        //  while the dialog is open, but the background (which we don't control here)
+        //  doesn't, which causes us to show things like white text on a white background.
+        //  as a workaround, we remember the original theme and keep it on recomposition.
+        val isCurrentlyInDarkTheme = isSystemInDarkTheme()
+        val cachedDarkTheme = remember { isCurrentlyInDarkTheme }
+        PlatformTheme(isDarkTheme = cachedDarkTheme) {
+            AlertDialogContent(
+                modifier = Modifier.semantics { testTagsAsResourceId = true },
+                title = {
+                    Text(
+                        modifier = Modifier.testTag("modes_title"),
+                        text = stringResource(R.string.zen_modes_dialog_title),
+                    )
+                },
+                content = { ModeTileGrid(viewModel.get()) },
+                neutralButton = {
+                    PlatformOutlinedButton(onClick = { openSettings(dialog) }) {
+                        Text(stringResource(R.string.zen_modes_dialog_settings))
+                    }
+                },
+                positiveButton = {
+                    PlatformButton(onClick = { dialog.dismiss() }) {
+                        Text(stringResource(R.string.zen_modes_dialog_done))
+                    }
+                },
+            )
+        }
     }
 
     @VisibleForTesting
@@ -129,7 +138,7 @@
         activityStarter.startActivity(
             ZEN_MODE_SETTINGS_INTENT,
             true /* dismissShade */,
-            animationController
+            animationController,
         )
     }
 
@@ -163,7 +172,7 @@
             Log.w(
                 TAG,
                 "Cannot launch from dialog, the dialog is not present. " +
-                    "Will launch activity without animating."
+                    "Will launch activity without animating.",
             )
         }
 
@@ -172,11 +181,7 @@
         if (animationController == null) {
             currentDialog?.dismiss()
         }
-        activityStarter.startActivity(
-            intent,
-            true, /* dismissShade */
-            animationController,
-        )
+        activityStarter.startActivity(intent, true, /* dismissShade */ animationController)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
index 27bc6d3..76389f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
@@ -71,7 +71,7 @@
                         .semantics { stateDescription = viewModel.stateDescription },
                 verticalAlignment = Alignment.CenterVertically,
                 horizontalArrangement =
-                    Arrangement.spacedBy(space = 8.dp, alignment = Alignment.Start),
+                    Arrangement.spacedBy(space = 12.dp, alignment = Alignment.Start),
             ) {
                 Icon(icon = viewModel.icon, modifier = Modifier.size(24.dp))
                 Column {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
index 5953ea5..5392e38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
@@ -26,7 +26,6 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.systemui.Flags
 import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
 
 @Composable
@@ -34,8 +33,8 @@
     val tiles by viewModel.tiles.collectAsStateWithLifecycle(initialValue = emptyList())
 
     LazyVerticalGrid(
-        columns = GridCells.Fixed(if (Flags.modesDialogSingleRows()) 1 else 2),
-        modifier = Modifier.fillMaxWidth().heightIn(max = 300.dp),
+        columns = GridCells.Fixed(1),
+        modifier = Modifier.fillMaxWidth().heightIn(max = 320.dp),
         verticalArrangement = Arrangement.spacedBy(8.dp),
         horizontalArrangement = Arrangement.spacedBy(8.dp),
     ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
index 8f424b2..1a1a592e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.statusbar.window
 
 import android.app.StatusBarManager
-import android.app.StatusBarManager.WindowVisibleState
 import android.app.StatusBarManager.WINDOW_STATE_SHOWING
 import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import android.app.StatusBarManager.WindowVisibleState
 import android.app.StatusBarManager.windowStateToString
 import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
@@ -31,23 +31,28 @@
 /**
  * A centralized class maintaining the state of the status bar window.
  *
+ * @deprecated use
+ *   [com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore.defaultDisplay]
+ *   repo instead.
+ *
  * Classes that want to get updates about the status bar window state should subscribe to this class
  * via [addListener] and should NOT add their own callback on [CommandQueue].
  */
 @SysUISingleton
-class StatusBarWindowStateController @Inject constructor(
-    @DisplayId private val thisDisplayId: Int,
-    commandQueue: CommandQueue
-) {
-    private val commandQueueCallback = object : CommandQueue.Callbacks {
-        override fun setWindowState(
-            displayId: Int,
-            @StatusBarManager.WindowType window: Int,
-            @WindowVisibleState state: Int
-        ) {
-            this@StatusBarWindowStateController.setWindowState(displayId, window, state)
+@Deprecated("Use StatusBarWindowStateRepositoryStore.defaultDisplay instead")
+class StatusBarWindowStateController
+@Inject
+constructor(@DisplayId private val thisDisplayId: Int, commandQueue: CommandQueue) {
+    private val commandQueueCallback =
+        object : CommandQueue.Callbacks {
+            override fun setWindowState(
+                displayId: Int,
+                @StatusBarManager.WindowType window: Int,
+                @WindowVisibleState state: Int,
+            ) {
+                this@StatusBarWindowStateController.setWindowState(displayId, window, state)
+            }
         }
-    }
     private val listeners: MutableSet<StatusBarWindowStateListener> = HashSet()
 
     @WindowVisibleState private var windowState: Int = WINDOW_STATE_SHOWING
@@ -71,7 +76,7 @@
     private fun setWindowState(
         displayId: Int,
         @StatusBarManager.WindowType window: Int,
-        @WindowVisibleState state: Int
+        @WindowVisibleState state: Int,
     ) {
         if (displayId != thisDisplayId) {
             return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepository.kt
new file mode 100644
index 0000000..bef8c84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepository.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.window.data.repository
+
+import android.app.StatusBarManager
+import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
+import android.app.StatusBarManager.WINDOW_STATE_HIDING
+import android.app.StatusBarManager.WINDOW_STATE_SHOWING
+import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import android.app.StatusBarManager.WindowVisibleState
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * A centralized class maintaining the state of the status bar window.
+ *
+ * Classes that want to get updates about the status bar window state should subscribe to
+ * [windowState] and should NOT add their own callback on [CommandQueue].
+ *
+ * Each concrete implementation of this class will be for a specific display ID. Use
+ * [StatusBarWindowStateRepositoryStore] to fetch a concrete implementation for a certain display.
+ */
+interface StatusBarWindowStatePerDisplayRepository {
+    /** Emits the current window state for the status bar on this specific display. */
+    val windowState: StateFlow<StatusBarWindowState>
+}
+
+class StatusBarWindowStatePerDisplayRepositoryImpl
+@AssistedInject
+constructor(
+    @Assisted private val thisDisplayId: Int,
+    private val commandQueue: CommandQueue,
+    @Application private val scope: CoroutineScope,
+) : StatusBarWindowStatePerDisplayRepository {
+    override val windowState: StateFlow<StatusBarWindowState> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : CommandQueue.Callbacks {
+                        override fun setWindowState(
+                            displayId: Int,
+                            @StatusBarManager.WindowType window: Int,
+                            @WindowVisibleState state: Int,
+                        ) {
+                            // TODO(b/364360986): Log the window state changes.
+                            if (displayId != thisDisplayId) {
+                                return
+                            }
+                            if (window != WINDOW_STATUS_BAR) {
+                                return
+                            }
+                            trySend(state.toWindowState())
+                        }
+                    }
+                commandQueue.addCallback(callback)
+                awaitClose { commandQueue.removeCallback(callback) }
+            }
+            // Use Eagerly because we always need to know about the status bar window state
+            .stateIn(scope, SharingStarted.Eagerly, StatusBarWindowState.Hidden)
+
+    @WindowVisibleState
+    private fun Int.toWindowState(): StatusBarWindowState {
+        return when (this) {
+            WINDOW_STATE_SHOWING -> StatusBarWindowState.Showing
+            WINDOW_STATE_HIDING -> StatusBarWindowState.Hiding
+            WINDOW_STATE_HIDDEN -> StatusBarWindowState.Hidden
+            else -> StatusBarWindowState.Hidden
+        }
+    }
+}
+
+@AssistedFactory
+interface StatusBarWindowStatePerDisplayRepositoryFactory {
+    fun create(@Assisted displayId: Int): StatusBarWindowStatePerDisplayRepositoryImpl
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStore.kt
new file mode 100644
index 0000000..0e33326
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStore.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.statusbar.window.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.DisplayId
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+
+/**
+ * Singleton class to create instances of [StatusBarWindowStatePerDisplayRepository] for a specific
+ * display.
+ *
+ * Repository instances for a specific display should be cached so that if multiple classes request
+ * a repository for the same display ID, we only create the repository once.
+ */
+interface StatusBarWindowStateRepositoryStore {
+    val defaultDisplay: StatusBarWindowStatePerDisplayRepository
+
+    fun forDisplay(displayId: Int): StatusBarWindowStatePerDisplayRepository
+}
+
+@SysUISingleton
+class StatusBarWindowStateRepositoryStoreImpl
+@Inject
+constructor(
+    @DisplayId private val displayId: Int,
+    private val factory: StatusBarWindowStatePerDisplayRepositoryFactory,
+) : StatusBarWindowStateRepositoryStore {
+    // Use WeakReferences to store the repositories so that the repositories are kept around so long
+    // as some UI holds a reference to them, but the repositories are cleaned up once no UI is using
+    // them anymore.
+    // See Change-Id Ib490062208506d646add2fe7e5e5d4df5fb3e66e for similar behavior in
+    // MobileConnectionsRepositoryImpl.
+    private val repositoryCache =
+        mutableMapOf<Int, WeakReference<StatusBarWindowStatePerDisplayRepository>>()
+
+    override val defaultDisplay = factory.create(displayId)
+
+    override fun forDisplay(displayId: Int): StatusBarWindowStatePerDisplayRepository {
+        synchronized(repositoryCache) {
+            return repositoryCache[displayId]?.get()
+                ?: factory.create(displayId).also { repositoryCache[displayId] = WeakReference(it) }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/shared/model/StatusBarWindowState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/shared/model/StatusBarWindowState.kt
new file mode 100644
index 0000000..a99046e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/shared/model/StatusBarWindowState.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.window.data.model
+
+/**
+ * Represents the state of the status bar *window* as a whole (as opposed to individual views within
+ * the status bar).
+ */
+enum class StatusBarWindowState {
+    Showing,
+    Hiding,
+    Hidden,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index a817b31..7bf9efa 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -195,7 +195,7 @@
 
                 val callback =
                     object : UserTracker.Callback {
-                        override fun onUserChanging(newUser: Int, userContext: Context) {
+                        override fun onBeforeUserSwitching(newUser: Int) {
                             send(SelectionStatus.SELECTION_IN_PROGRESS)
                         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
index 8ecf250..2af84c7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
@@ -37,7 +37,7 @@
     @Application
     fun applicationScope(
         @Main dispatcherContext: CoroutineContext,
-    ): CoroutineScope = CoroutineScope(dispatcherContext)
+    ): CoroutineScope = CoroutineScope(dispatcherContext + createCoroutineTracingContext("ApplicationScope"))
 
     @Provides
     @Singleton
@@ -51,15 +51,7 @@
     @Provides
     @Singleton
     @Main
-    fun mainCoroutineContext(@Tracing tracingCoroutineContext: CoroutineContext): CoroutineContext {
-        return Dispatchers.Main.immediate + tracingCoroutineContext
-    }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Provides
-    @Tracing
-    @Singleton
-    fun tracingCoroutineContext(): CoroutineContext {
-        return createCoroutineTracingContext()
+    fun mainCoroutineContext(): CoroutineContext {
+        return Dispatchers.Main.immediate
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
index a03221e..3c06828 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
@@ -91,10 +91,9 @@
     @Background
     @SysUISingleton
     fun bgCoroutineContext(
-        @Tracing tracingCoroutineContext: CoroutineContext,
         @Background bgCoroutineDispatcher: CoroutineDispatcher,
     ): CoroutineContext {
-        return bgCoroutineDispatcher + tracingCoroutineContext
+        return bgCoroutineDispatcher
     }
 
     /** Coroutine dispatcher for background operations on for UI. */
@@ -112,9 +111,8 @@
     @UiBackground
     @SysUISingleton
     fun uiBgCoroutineContext(
-        @Tracing tracingCoroutineContext: CoroutineContext,
         @UiBackground uiBgCoroutineDispatcher: CoroutineDispatcher,
     ): CoroutineContext {
-        return uiBgCoroutineDispatcher + tracingCoroutineContext
+        return uiBgCoroutineDispatcher
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index 82f41a7..4d9aaa6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.util.settings
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.annotation.UserIdInt
 import android.content.ContentResolver
 import android.database.ContentObserver
@@ -93,7 +94,7 @@
      */
     @AnyThread
     fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-A")).launch {
             registerContentObserverSync(getUriFor(name), settingsObserver)
         }
 
@@ -110,7 +111,7 @@
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-B")).launch {
             registerContentObserverSync(getUriFor(name), settingsObserver)
             registered.run()
         }
@@ -143,7 +144,7 @@
      */
     @AnyThread
     fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-C")).launch {
             registerContentObserverSync(uri, settingsObserver)
         }
 
@@ -160,7 +161,7 @@
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-D")).launch {
             registerContentObserverSync(uri, settingsObserver)
             registered.run()
         }
@@ -205,7 +206,7 @@
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-E")).launch {
             registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
         }
 
@@ -223,7 +224,7 @@
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-F")).launch {
             registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
             registered.run()
         }
@@ -274,7 +275,7 @@
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-G")).launch {
             registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
         }
 
@@ -292,7 +293,7 @@
         settingsObserver: ContentObserver,
         @WorkerThread registered: Runnable
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-H")).launch {
             registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
             registered.run()
         }
@@ -329,7 +330,7 @@
      */
     @AnyThread
     fun unregisterContentObserverAsync(settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher).launch { unregisterContentObserver(settingsObserver) }
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-I")).launch { unregisterContentObserver(settingsObserver) }
 
     /**
      * Look up a name in the database.
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index 8e3b813..c820c07 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.util.settings
 
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
 import android.annotation.UserIdInt
 import android.annotation.WorkerThread
 import android.content.ContentResolver
@@ -78,7 +79,7 @@
     }
 
     override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-A")).launch {
             registerContentObserverForUserSync(uri, settingsObserver, userId)
         }
 
@@ -112,7 +113,7 @@
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-B")).launch {
             registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
         }
 
@@ -157,7 +158,7 @@
         settingsObserver: ContentObserver,
         userHandle: Int
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-C")).launch {
             registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
         }
 
@@ -198,7 +199,7 @@
         settingsObserver: ContentObserver,
         userHandle: Int
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-D")).launch {
             registerContentObserverForUserSync(uri, settingsObserver, userHandle)
         }
 
@@ -215,7 +216,7 @@
         userHandle: Int,
         @WorkerThread registered: Runnable
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-E")).launch {
             registerContentObserverForUserSync(uri, settingsObserver, userHandle)
             registered.run()
         }
@@ -274,7 +275,7 @@
         settingsObserver: ContentObserver,
         userHandle: Int
     ) {
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-F")).launch {
             registerContentObserverForUserSync(
                 getUriFor(name),
                 notifyForDescendants,
@@ -338,7 +339,7 @@
         settingsObserver: ContentObserver,
         userHandle: Int
     ) =
-        CoroutineScope(backgroundDispatcher).launch {
+        CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-G")).launch {
             registerContentObserverForUserSync(
                 uri,
                 notifyForDescendants,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index a94ef36..c7b707d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -17,11 +17,16 @@
 
 import android.content.BroadcastReceiver
 import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
 import android.view.View
 import android.view.ViewTreeObserver
 import android.widget.FrameLayout
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_ACTIVE
+import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_INACTIVE
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.Flags
@@ -36,6 +41,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.core.LogcatOnlyMessageBuffer
 import com.android.systemui.plugins.clocks.ClockAnimations
@@ -46,9 +52,15 @@
 import com.android.systemui.plugins.clocks.ClockFaceEvents
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
 import com.android.systemui.plugins.clocks.ClockTickRate
+import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.plugins.clocks.ZenData.ZenMode
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -57,9 +69,12 @@
 import com.android.systemui.util.mockito.mock
 import java.util.TimeZone
 import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import kotlinx.coroutines.yield
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -73,15 +88,26 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import com.android.systemui.Flags as AConfigFlags
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
 class ClockEventControllerTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val zenModeRepository = kosmos.fakeZenModeRepository
+    private val testScope = kosmos.testScope
+
     @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+    private val mainExecutor = ImmediateExecutor()
+    private lateinit var repository: FakeKeyguardRepository
+    private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
+    private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
+    private lateinit var underTest: ClockEventController
+
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
     @Mock private lateinit var batteryController: BatteryController
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -89,7 +115,6 @@
     @Mock private lateinit var animations: ClockAnimations
     @Mock private lateinit var events: ClockEvents
     @Mock private lateinit var clock: ClockController
-    @Mock private lateinit var mainExecutor: DelayableExecutor
     @Mock private lateinit var bgExecutor: Executor
     @Mock private lateinit var smallClockController: ClockFaceController
     @Mock private lateinit var smallClockView: View
@@ -102,12 +127,10 @@
     @Mock private lateinit var smallClockEvents: ClockFaceEvents
     @Mock private lateinit var largeClockEvents: ClockFaceEvents
     @Mock private lateinit var parentView: View
-    private lateinit var repository: FakeKeyguardRepository
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
-    private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
-    private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
-    private lateinit var underTest: ClockEventController
+
     @Mock private lateinit var zenModeController: ZenModeController
+    private var zenModeControllerCallback: ZenModeController.Callback? = null
 
     @Before
     fun setUp() {
@@ -129,12 +152,11 @@
         whenever(largeClockController.config)
             .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
 
+        zenModeRepository.addMode(MANUAL_DND_INACTIVE)
+
         repository = FakeKeyguardRepository()
 
-        val withDeps =
-            KeyguardInteractorFactory.create(
-                repository = repository,
-            )
+        val withDeps = KeyguardInteractorFactory.create(repository = repository)
 
         withDeps.featureFlags.apply { set(Flags.REGION_SAMPLING, false) }
         underTest =
@@ -151,7 +173,8 @@
                 bgExecutor,
                 clockBuffers,
                 withDeps.featureFlags,
-                zenModeController
+                zenModeController,
+                kosmos.zenModeInteractor,
             )
         underTest.clock = clock
 
@@ -161,6 +184,10 @@
             repository.setIsDozing(true)
             repository.setDozeAmount(1f)
         }
+
+        val zenCallbackCaptor = argumentCaptor<ZenModeController.Callback>()
+        verify(zenModeController).addCallback(zenCallbackCaptor.capture())
+        zenModeControllerCallback = zenCallbackCaptor.value
     }
 
     @Test
@@ -349,17 +376,12 @@
     fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor
-                .transition(Edge.create(to = AOD)))
+            whenever(keyguardTransitionInteractor.transition(Edge.create(to = AOD)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToAodTransition(this)
             transitionStep.value =
-                TransitionStep(
-                    from = GONE,
-                    to = AOD,
-                    transitionState = TransitionState.STARTED,
-                )
+                TransitionStep(from = GONE, to = AOD, transitionState = TransitionState.STARTED)
             yield()
 
             verify(animations, times(2)).doze(1f)
@@ -371,8 +393,7 @@
     fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor
-                .transition(Edge.create(to = LOCKSCREEN)))
+            whenever(keyguardTransitionInteractor.transition(Edge.create(to = LOCKSCREEN)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -393,8 +414,7 @@
     fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor
-                .transition(Edge.create(to = AOD)))
+            whenever(keyguardTransitionInteractor.transition(Edge.create(to = AOD)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToAodTransition(this)
@@ -415,8 +435,7 @@
     fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor
-                .transition(Edge.create(to = LOCKSCREEN)))
+            whenever(keyguardTransitionInteractor.transition(Edge.create(to = LOCKSCREEN)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -437,8 +456,7 @@
     fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor
-                .transition(Edge.create(to = DOZING)))
+            whenever(keyguardTransitionInteractor.transition(Edge.create(to = DOZING)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToDozingTransition(this)
@@ -498,7 +516,57 @@
             verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
         }
 
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_UI)
+    fun listenForDnd_onDndChange_updatesClockZenMode() =
+        testScope.runTest {
+            underTest.listenForDnd(testScope.backgroundScope)
+
+            zenModeRepository.replaceMode(MANUAL_DND_INACTIVE.id, MANUAL_DND_ACTIVE)
+            runCurrent()
+
+            verify(events)
+                .onZenDataChanged(
+                    eq(ZenData(ZenMode.IMPORTANT_INTERRUPTIONS, R.string::dnd_is_on.name))
+                )
+
+            zenModeRepository.replaceMode(MANUAL_DND_ACTIVE.id, MANUAL_DND_INACTIVE)
+            runCurrent()
+
+            verify(events).onZenDataChanged(eq(ZenData(ZenMode.OFF, R.string::dnd_is_off.name)))
+        }
+
+    @Test
+    @DisableFlags(android.app.Flags.FLAG_MODES_UI)
+    fun zenModeControllerCallback_onDndChange_updatesClockZenMode() =
+        runBlocking(IMMEDIATE) {
+            zenModeControllerCallback!!.onZenChanged(
+                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+            )
+
+            verify(events)
+                .onZenDataChanged(
+                    eq(ZenData(ZenMode.IMPORTANT_INTERRUPTIONS, R.string::dnd_is_on.name))
+                )
+
+            zenModeControllerCallback!!.onZenChanged(Settings.Global.ZEN_MODE_OFF)
+
+            verify(events).onZenDataChanged(eq(ZenData(ZenMode.OFF, R.string::dnd_is_off.name)))
+        }
+
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
     }
 }
+
+private class ImmediateExecutor : DelayableExecutor {
+    override fun execute(runnable: Runnable) {
+        runnable.run()
+    }
+
+    override fun executeDelayed(runnable: Runnable, delay: Long, unit: TimeUnit) =
+        runnable.apply { run() }
+
+    override fun executeAtTime(runnable: Runnable, uptimeMillis: Long, unit: TimeUnit) =
+        runnable.apply { run() }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index e444db4..5fe5cb3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -76,6 +76,7 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController
 import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.kotlin.JavaAdapter
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argThat
@@ -84,6 +85,7 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth
 import junit.framework.Assert
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -172,6 +174,7 @@
     @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
     private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState>
     private lateinit var fakeSceneDataSource: FakeSceneDataSource
+    private val executor = FakeExecutor(FakeSystemClock())
 
     private lateinit var underTest: KeyguardSecurityContainerController
 
@@ -210,13 +213,9 @@
         featureFlags = FakeFeatureFlags()
         featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
 
-        mSetFlagsRule.enableFlags(
-            AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES,
-        )
+        mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
         if (!SceneContainerFlag.isEnabled) {
-            mSetFlagsRule.disableFlags(
-                AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
-            )
+            mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
         }
 
         keyguardPasswordViewController =
@@ -239,7 +238,7 @@
                 mSelectedUserInteractor,
                 keyguardKeyboardInteractor,
                 null,
-                mUserActivityNotifier
+                mUserActivityNotifier,
             )
 
         kosmos = testKosmos()
@@ -283,6 +282,7 @@
                 devicePolicyManager,
                 kosmos.keyguardDismissTransitionInteractor,
                 { primaryBouncerInteractor },
+                executor,
             ) {
                 deviceEntryInteractor
             }
@@ -298,7 +298,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -334,7 +334,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
 
         // Update rotation. Should trigger update
@@ -347,7 +347,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -359,7 +359,7 @@
                 MotionEvent.ACTION_DOWN,
                 /* x= */ 0f,
                 /* y= */ 0f,
-                /* metaState= */ 0
+                /* metaState= */ 0,
             )
         )
     }
@@ -386,7 +386,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -401,7 +401,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -416,7 +416,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -431,7 +431,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -446,7 +446,7 @@
                 eq(falsingManager),
                 eq(userSwitcherController),
                 any(),
-                eq(falsingA11yDelegate)
+                eq(falsingA11yDelegate),
             )
     }
 
@@ -462,7 +462,7 @@
             .showMessage(
                 /* message= */ context.getString(R.string.keyguard_unlock_to_continue),
                 /* colorState= */ null,
-                /* animated= */ true
+                /* animated= */ true,
             )
     }
 
@@ -496,7 +496,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN the next security method of None will dismiss keyguard.
@@ -514,7 +514,7 @@
                 /* authenticated= */ true,
                 TARGET_USER_ID,
                 /* bypassSecondaryLockScreen= */ true,
-                SecurityMode.SimPin
+                SecurityMode.SimPin,
             )
 
         // THEN no action has happened, which will not dismiss the security screens
@@ -539,7 +539,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN the next security method of None will dismiss keyguard.
@@ -564,7 +564,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN the next security method of None will dismiss keyguard.
@@ -589,7 +589,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN we will not show the password screen.
@@ -615,7 +615,7 @@
             /* authenticated= */ true,
             TARGET_USER_ID,
             /* bypassSecondaryLockScreen= */ true,
-            SecurityMode.SimPin
+            SecurityMode.SimPin,
         )
 
         // THEN we will not show the password screen.
@@ -717,7 +717,7 @@
         // Now simulate a config change
         testableResources.addOverride(
             R.integer.keyguard_host_view_gravity,
-            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
+            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM,
         )
         underTest.updateResources()
         verify(view).layoutParams = any()
@@ -728,7 +728,7 @@
         testableResources.addOverride(R.integer.keyguard_host_view_gravity, Gravity.CENTER)
         testableResources.addOverride(
             R.integer.keyguard_host_view_one_handed_gravity,
-            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
+            Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM,
         )
 
         // Start disabled.
@@ -948,7 +948,7 @@
             /* expiringUserId = */ mainUserId,
             /* mainUserId = */ mainUserId,
             /* remainingBeforeWipe = */ 1,
-            /* failedAttempts = */ 1
+            /* failedAttempts = */ 1,
         )
 
         verify(view)
@@ -965,14 +965,14 @@
             /* expiringUserId = */ secondaryUserId,
             /* mainUserId = */ mainUserId,
             /* remainingBeforeWipe = */ 1,
-            /* failedAttempts = */ 1
+            /* failedAttempts = */ 1,
         )
 
         verify(view)
             .showAlmostAtWipeDialog(
                 any(),
                 any(),
-                eq(KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER)
+                eq(KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER),
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
rename to packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index bbff539..6dc4b10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -18,10 +18,6 @@
 
 import android.graphics.Point
 import android.hardware.biometrics.BiometricSourceType
-import android.hardware.biometrics.ComponentInfoInternal
-import android.hardware.biometrics.SensorLocationInternal
-import android.hardware.biometrics.SensorProperties
-import android.hardware.fingerprint.FingerprintSensorProperties
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
 import android.testing.TestableLooper.RunWithLooper
 import android.util.DisplayMetrics
@@ -47,7 +43,6 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.leak.RotationUtils
 import com.android.systemui.util.mockito.any
-import javax.inject.Provider
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.After
 import org.junit.Assert.assertFalse
@@ -67,6 +62,8 @@
 import org.mockito.MockitoAnnotations
 import org.mockito.MockitoSession
 import org.mockito.quality.Strictness
+import javax.inject.Provider
+
 
 @ExperimentalCoroutinesApi
 @SmallTest
@@ -82,28 +79,35 @@
     @Mock private lateinit var authController: AuthController
     @Mock private lateinit var authRippleInteractor: AuthRippleInteractor
     @Mock private lateinit var keyguardStateController: KeyguardStateController
-    @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
-    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
-    @Mock private lateinit var biometricUnlockController: BiometricUnlockController
-    @Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
-    @Mock private lateinit var udfpsController: UdfpsController
-    @Mock private lateinit var statusBarStateController: StatusBarStateController
-    @Mock private lateinit var lightRevealScrim: LightRevealScrim
-    @Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
+    @Mock
+    private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+    @Mock
+    private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock
+    private lateinit var biometricUnlockController: BiometricUnlockController
+    @Mock
+    private lateinit var udfpsControllerProvider: Provider<UdfpsController>
+    @Mock
+    private lateinit var udfpsController: UdfpsController
+    @Mock
+    private lateinit var statusBarStateController: StatusBarStateController
+    @Mock
+    private lateinit var lightRevealScrim: LightRevealScrim
+    @Mock
+    private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
 
     private val facePropertyRepository = FakeFacePropertyRepository()
     private val displayMetrics = DisplayMetrics()
 
     @Captor
     private lateinit var biometricUnlockListener:
-        ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
+            ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
 
     @Before
     fun setUp() {
         mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
         MockitoAnnotations.initMocks(this)
-        staticMockSession =
-            mockitoSession()
+        staticMockSession = mockitoSession()
                 .mockStatic(RotationUtils::class.java)
                 .strictness(Strictness.LENIENT)
                 .startMocking()
@@ -112,26 +116,25 @@
         `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp))
         `when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
 
-        controller =
-            AuthRippleController(
-                context,
-                authController,
-                configurationController,
-                keyguardUpdateMonitor,
-                keyguardStateController,
-                wakefulnessLifecycle,
-                commandRegistry,
-                notificationShadeWindowController,
-                udfpsControllerProvider,
-                statusBarStateController,
-                displayMetrics,
-                KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
-                biometricUnlockController,
-                lightRevealScrim,
-                authRippleInteractor,
-                facePropertyRepository,
-                rippleView,
-            )
+        controller = AuthRippleController(
+            context,
+            authController,
+            configurationController,
+            keyguardUpdateMonitor,
+            keyguardStateController,
+            wakefulnessLifecycle,
+            commandRegistry,
+            notificationShadeWindowController,
+            udfpsControllerProvider,
+            statusBarStateController,
+            displayMetrics,
+            KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
+            biometricUnlockController,
+            lightRevealScrim,
+            authRippleInteractor,
+            facePropertyRepository,
+            rippleView,
+        )
         controller.init()
     }
 
@@ -147,18 +150,13 @@
         `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
         controller.onViewAttached()
         `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(
-                keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                    eq(BiometricSourceType.FINGERPRINT)
-                )
-            )
-            .thenReturn(true)
+        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
 
         // WHEN fingerprint authenticated
         verify(biometricUnlockController).addListener(biometricUnlockListener.capture())
-        biometricUnlockListener.value.onBiometricUnlockedWithKeyguardDismissal(
-            BiometricSourceType.FINGERPRINT
-        )
+        biometricUnlockListener.value
+                .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT)
 
         // THEN update sensor location and show ripple
         verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
@@ -171,12 +169,8 @@
         val fpsLocation = Point(5, 5)
         `when`(authController.udfpsLocation).thenReturn(fpsLocation)
         controller.onViewAttached()
-        `when`(
-                keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                    eq(BiometricSourceType.FINGERPRINT)
-                )
-            )
-            .thenReturn(true)
+        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
 
         // WHEN keyguard is NOT showing & fingerprint authenticated
         `when`(keyguardStateController.isShowing).thenReturn(false)
@@ -185,8 +179,7 @@
         captor.value.onBiometricAuthenticated(
             0 /* userId */,
             BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */
-        )
+            false /* isStrongBiometric */)
 
         // THEN no ripple
         verify(rippleView, never()).startUnlockedRipple(any())
@@ -201,19 +194,14 @@
         `when`(keyguardStateController.isShowing).thenReturn(true)
 
         // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated
-        `when`(
-                keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                    eq(BiometricSourceType.FINGERPRINT)
-                )
-            )
-            .thenReturn(false)
+        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                eq(BiometricSourceType.FINGERPRINT))).thenReturn(false)
         val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
         verify(keyguardUpdateMonitor).registerCallback(captor.capture())
         captor.value.onBiometricAuthenticated(
             0 /* userId */,
             BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */
-        )
+            false /* isStrongBiometric */)
 
         // THEN no ripple
         verify(rippleView, never()).startUnlockedRipple(any())
@@ -230,8 +218,7 @@
         captor.value.onBiometricAuthenticated(
             0 /* userId */,
             BiometricSourceType.FACE /* type */,
-            false /* isStrongBiometric */
-        )
+            false /* isStrongBiometric */)
         verify(rippleView, never()).startUnlockedRipple(any())
     }
 
@@ -246,17 +233,18 @@
         captor.value.onBiometricAuthenticated(
             0 /* userId */,
             BiometricSourceType.FINGERPRINT /* type */,
-            false /* isStrongBiometric */
-        )
+            false /* isStrongBiometric */)
         verify(rippleView, never()).startUnlockedRipple(any())
     }
 
     @Test
     fun registersAndDeregisters() {
         controller.onViewAttached()
-        val captor = ArgumentCaptor.forClass(KeyguardStateController.Callback::class.java)
+        val captor = ArgumentCaptor
+            .forClass(KeyguardStateController.Callback::class.java)
         verify(keyguardStateController).addCallback(captor.capture())
-        val captor2 = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java)
+        val captor2 = ArgumentCaptor
+            .forClass(WakefulnessLifecycle.Observer::class.java)
         verify(wakefulnessLifecycle).addObserver(captor2.capture())
         controller.onViewDetached()
         verify(keyguardStateController).removeCallback(any())
@@ -271,25 +259,17 @@
         `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
         controller.onViewAttached()
         `when`(keyguardStateController.isShowing).thenReturn(true)
-        `when`(
-                keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
-                    BiometricSourceType.FINGERPRINT
-                )
-            )
-            .thenReturn(true)
+        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                BiometricSourceType.FINGERPRINT)).thenReturn(true)
         `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
 
         controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
-        assertTrue(
-            "reveal didn't start on keyguardFadingAway",
-            controller.startLightRevealScrimOnKeyguardFadingAway
-        )
+        assertTrue("reveal didn't start on keyguardFadingAway",
+            controller.startLightRevealScrimOnKeyguardFadingAway)
         `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
         controller.onKeyguardFadingAwayChanged()
-        assertFalse(
-            "reveal triggers multiple times",
-            controller.startLightRevealScrimOnKeyguardFadingAway
-        )
+        assertFalse("reveal triggers multiple times",
+            controller.startLightRevealScrimOnKeyguardFadingAway)
     }
 
     @Test
@@ -302,27 +282,23 @@
         `when`(keyguardStateController.isShowing).thenReturn(true)
         `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
         `when`(authController.isUdfpsFingerDown).thenReturn(true)
-        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(eq(BiometricSourceType.FACE)))
-            .thenReturn(true)
+        `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                eq(BiometricSourceType.FACE))).thenReturn(true)
 
         controller.showUnlockRipple(BiometricSourceType.FACE)
-        assertTrue(
-            "reveal didn't start on keyguardFadingAway",
-            controller.startLightRevealScrimOnKeyguardFadingAway
-        )
+        assertTrue("reveal didn't start on keyguardFadingAway",
+                controller.startLightRevealScrimOnKeyguardFadingAway)
         `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
         controller.onKeyguardFadingAwayChanged()
-        assertFalse(
-            "reveal triggers multiple times",
-            controller.startLightRevealScrimOnKeyguardFadingAway
-        )
+        assertFalse("reveal triggers multiple times",
+                controller.startLightRevealScrimOnKeyguardFadingAway)
     }
 
     @Test
     fun testUpdateRippleColor() {
         controller.onViewAttached()
-        val captor =
-            ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
+        val captor = ArgumentCaptor
+            .forClass(ConfigurationController.ConfigurationListener::class.java)
         verify(configurationController).addCallback(captor.capture())
 
         reset(rippleView)
@@ -357,40 +333,6 @@
     }
 
     @Test
-    fun testUltrasonicUdfps_onFingerDown_runningForDeviceEntry_doNotShowDwellRipple() {
-        // GIVEN UDFPS is ultrasonic
-        `when`(authController.udfpsProps)
-            .thenReturn(
-                listOf(
-                    FingerprintSensorPropertiesInternal(
-                        0 /* sensorId */,
-                        SensorProperties.STRENGTH_STRONG,
-                        5 /* maxEnrollmentsPerUser */,
-                        listOf<ComponentInfoInternal>(),
-                        FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC,
-                        false /* halControlsIllumination */,
-                        true /* resetLockoutRequiresHardwareAuthToken */,
-                        listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
-                    )
-                )
-            )
-
-        // GIVEN fingerprint detection is running on keyguard
-        `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true)
-
-        // GIVEN view is already attached
-        controller.onViewAttached()
-        val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
-        verify(udfpsController).addCallback(captor.capture())
-
-        // WHEN finger is down
-        captor.value.onFingerDown()
-
-        // THEN never show dwell ripple
-        verify(rippleView, never()).startDwellRipple(false)
-    }
-
-    @Test
     fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() {
         // GIVEN fingerprint detection is NOT running on keyguard
         `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS
index 5420c37..58ae2b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS
@@ -3,3 +3,4 @@
 # Bug component: 879035
 include /services/core/java/com/android/server/biometrics/OWNERS
 beverlyt@google.com
+include /packages/SystemUI/TEST_OWNERS
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 4850510..55fd344 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -54,7 +54,6 @@
 import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
 import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
-import com.android.systemui.biometrics.domain.interactor.sideFpsOverlayInteractor
 import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
 import com.android.systemui.biometrics.extractAuthenticatorTypes
 import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -1454,15 +1453,11 @@
     @Test
     fun switch_to_credential_fallback() = runGenericTest {
         val size by collectLastValue(kosmos.promptViewModel.size)
-        val isShowingSfpsIndicator by collectLastValue(kosmos.sideFpsOverlayInteractor.isShowing)
 
         // TODO(b/251476085): remove Spaghetti, migrate logic, and update this test
         kosmos.promptViewModel.onSwitchToCredential()
 
         assertThat(size).isEqualTo(PromptSize.LARGE)
-        if (testCase.modalities.hasSfps) {
-            assertThat(isShowingSfpsIndicator).isFalse()
-        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
index 22946c8..5cc6454 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
@@ -62,6 +62,7 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.scene.sceneContainerGestureFilterFactory
 import com.android.systemui.scene.shared.logger.sceneLogger
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
@@ -70,8 +71,10 @@
 import com.android.systemui.scene.ui.composable.SceneContainer
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
 import com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
+import com.android.systemui.settings.displayTracker
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.testKosmos
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -115,11 +118,7 @@
     private val Kosmos.sceneKeys by Fixture { listOf(Scenes.Lockscreen, Scenes.Bouncer) }
     private val Kosmos.initialSceneKey by Fixture { Scenes.Bouncer }
     private val Kosmos.sceneContainerConfig by Fixture {
-        val navigationDistances =
-            mapOf(
-                Scenes.Lockscreen to 1,
-                Scenes.Bouncer to 0,
-            )
+        val navigationDistances = mapOf(Scenes.Lockscreen to 1, Scenes.Bouncer to 0)
         SceneContainerConfig(sceneKeys, initialSceneKey, emptyList(), navigationDistances)
     }
 
@@ -136,6 +135,8 @@
                 shadeInteractor = kosmos.shadeInteractor,
                 splitEdgeDetector = kosmos.splitEdgeDetector,
                 logger = kosmos.sceneLogger,
+                gestureFilterFactory = kosmos.sceneContainerGestureFilterFactory,
+                displayId = kosmos.displayTracker.defaultDisplayId,
                 motionEventHandlerReceiver = {},
             )
             .apply { setTransitionState(transitionState) }
@@ -160,7 +161,7 @@
         BouncerScene(
             bouncerSceneActionsViewModelFactory,
             bouncerSceneContentViewModelFactory,
-            bouncerDialogFactory
+            bouncerDialogFactory,
         )
 
     @Before
@@ -175,7 +176,7 @@
 
     @Test
     fun bouncerPredictiveBackMotion() =
-        motionTestRule.runTest {
+        motionTestRule.runTest(timeout = 30.seconds) {
             val motion =
                 recordMotion(
                     content = { play ->
@@ -189,11 +190,11 @@
                                 sceneByKey =
                                     mapOf(
                                         Scenes.Lockscreen to FakeLockscreen(),
-                                        Scenes.Bouncer to bouncerScene
+                                        Scenes.Bouncer to bouncerScene,
                                     ),
                                 initialSceneKey = Scenes.Bouncer,
                                 overlayByKey = emptyMap(),
-                                dataSourceDelegator = kosmos.sceneDataSourceDelegator
+                                dataSourceDelegator = kosmos.sceneDataSourceDelegator,
                             )
                         }
                     },
@@ -215,14 +216,14 @@
                         feature(
                             isElement(Bouncer.Elements.Content),
                             positionInRoot,
-                            "content_offset"
+                            "content_offset",
                         )
                         feature(
                             isElement(Bouncer.Elements.Background),
                             elementAlpha,
-                            "background_alpha"
+                            "background_alpha",
                         )
-                    }
+                    },
                 )
 
             assertThat(motion).timeSeriesMatchesGolden()
@@ -240,7 +241,7 @@
                 }
                 backProgress.animateTo(
                     targetValue = 1f,
-                    animationSpec = tween(durationMillis = 500)
+                    animationSpec = tween(durationMillis = 500),
                 ) {
                     androidComposeTestRule.runOnUiThread {
                         dispatcher.dispatchOnBackProgressed(
@@ -309,10 +310,10 @@
                                         is JSONObject ->
                                             Offset(
                                                 pivot.getDouble("x").toFloat(),
-                                                pivot.getDouble("y").toFloat()
+                                                pivot.getDouble("y").toFloat(),
                                             )
                                         else -> throw UnknownTypeException()
-                                    }
+                                    },
                             )
                         }
                         else -> throw UnknownTypeException()
@@ -337,12 +338,12 @@
                                                 put("x", it.pivot.x)
                                                 put("y", it.pivot.y)
                                             }
-                                    }
+                                    },
                                 )
                             }
                         }
                     }
-                }
+                },
             )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
index 5d76e32..85e8ab4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
@@ -22,10 +22,12 @@
 import android.graphics.Bitmap
 import android.net.Uri
 import android.os.PersistableBundle
+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_CLIPBOARD_USE_DESCRIPTION_MIMETYPE
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.whenever
 import java.io.IOException
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
@@ -37,6 +39,7 @@
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -88,7 +91,8 @@
 
     @Test
     @Throws(IOException::class)
-    fun test_imageClipData() {
+    @DisableFlags(FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE)
+    fun test_imageClipData_legacy() {
         val testBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888)
         whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
         whenever(mMockContext.resources).thenReturn(mContext.resources)
@@ -103,6 +107,21 @@
 
     @Test
     @Throws(IOException::class)
+    @EnableFlags(FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE)
+    fun test_imageClipData() {
+        val testBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888)
+        whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
+        whenever(mMockContext.resources).thenReturn(mContext.resources)
+        whenever(mMockContentResolver.loadThumbnail(any(), any(), any())).thenReturn(testBitmap)
+        whenever(mMockContentResolver.getType(any())).thenReturn("text")
+        val imageClipData = ClipData("Test", arrayOf("image/png"), ClipData.Item(Uri.parse("test")))
+        val model = ClipboardModel.fromClipData(mMockContext, mClipboardUtils, imageClipData, "")
+        assertEquals(ClipboardModel.Type.IMAGE, model.type)
+        assertEquals(testBitmap, model.loadThumbnail(mMockContext))
+    }
+
+    @Test
+    @Throws(IOException::class)
     fun test_imageClipData_loadFailure() {
         whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
         whenever(mMockContext.resources).thenReturn(mContext.resources)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 633efd8..6c6de61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -483,6 +483,16 @@
             assertThat(values.toIdSets()).containsExactly(setOf(0))
         }
 
+    @Test
+    fun displayIdToId() =
+        testScope.runTest {
+            setDisplays(0, 1)
+
+            assertThat(displayRepository.getDisplay(0)?.displayId).isEqualTo(0)
+            assertThat(displayRepository.getDisplay(1)?.displayId).isEqualTo(1)
+            assertThat(displayRepository.getDisplay(2)).isNull()
+        }
+
     private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
 
     private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 7dd7174..a3314e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -185,7 +185,7 @@
                 dreamsComplicationComponent,
                 dreamOverlayComponent,
                 complicationComponent,
-                ambientTouchComponent
+                ambientTouchComponent,
             )
         }
 
@@ -194,7 +194,7 @@
                 dreamsComplicationComponent,
                 dreamOverlayComponent,
                 complicationComponent,
-                ambientTouchComponent
+                ambientTouchComponent,
             )
         }
     }
@@ -204,7 +204,7 @@
             com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory,
         dreamOverlayComponentFactory: DreamOverlayComponent.Factory,
         complicationComponentFactory: ComplicationComponent.Factory,
-        ambientTouchComponentFactory: AmbientTouchComponent.Factory
+        ambientTouchComponentFactory: AmbientTouchComponent.Factory,
     ): EnvironmentComponents {
         val dreamOverlayComponent = mock<DreamOverlayComponent>()
         whenever(dreamOverlayComponent.getDreamOverlayContainerViewController())
@@ -241,7 +241,7 @@
             dreamComplicationComponent,
             dreamOverlayComponent,
             complicationComponent,
-            ambientTouchComponent
+            ambientTouchComponent,
         )
     }
 
@@ -259,7 +259,7 @@
                 mDreamComplicationComponentFactory,
                 mDreamOverlayComponentFactory,
                 mComplicationComponentFactory,
-                mAmbientTouchComponentFactory
+                mAmbientTouchComponentFactory,
             )
 
         whenever(mDreamOverlayContainerViewController.containerView)
@@ -271,7 +271,7 @@
             ViewCaptureAwareWindowManager(
                 mWindowManager,
                 mLazyViewCapture,
-                isViewCaptureEnabled = false
+                isViewCaptureEnabled = false,
             )
         mService =
             DreamOverlayService(
@@ -295,7 +295,7 @@
                 mDreamOverlayCallbackController,
                 kosmos.keyguardInteractor,
                 gestureInteractor,
-                WINDOW_NAME
+                WINDOW_NAME,
             )
     }
 
@@ -323,7 +323,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START)
@@ -341,7 +341,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         verify(mWindowManager).addView(any(), any())
@@ -361,7 +361,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         verify(mWindowManager).addView(any(), any())
@@ -384,7 +384,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         verify(mDreamOverlayContainerViewController).init()
@@ -403,7 +403,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView)
@@ -419,7 +419,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(mService.shouldShowComplications()).isTrue()
@@ -435,7 +435,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -448,7 +448,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
 
         mMainExecutor.runAllReady()
@@ -476,7 +476,7 @@
             mDreamOverlayCallback,
             LOW_LIGHT_COMPONENT.flattenToString(),
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(mService.dreamComponent).isEqualTo(LOW_LIGHT_COMPONENT)
@@ -493,7 +493,7 @@
             mDreamOverlayCallback,
             HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(),
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(mService.dreamComponent).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT)
@@ -510,7 +510,7 @@
             mDreamOverlayCallback,
             LOW_LIGHT_COMPONENT.flattenToString(),
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -545,7 +545,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         // Immediately end the dream.
         client.endDream()
@@ -577,7 +577,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -597,7 +597,7 @@
             mDreamOverlayCallback,
             LOW_LIGHT_COMPONENT.flattenToString(),
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -649,7 +649,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         verify(mWindowManager, Mockito.never()).addView(any(), any())
@@ -673,7 +673,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -694,7 +694,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -724,7 +724,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         mService.onWakeUp()
@@ -748,7 +748,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         val paramsCaptor = ArgumentCaptor.forClass(WindowManager.LayoutParams::class.java)
@@ -774,7 +774,7 @@
                 mDreamOverlayCallback,
                 DREAM_COMPONENT,
                 false /*isPreview*/,
-                false /*shouldShowComplication*/
+                false, /*shouldShowComplication*/
             )
             mMainExecutor.runAllReady()
 
@@ -800,7 +800,7 @@
                 mDreamOverlayCallback,
                 DREAM_COMPONENT,
                 false /*isPreview*/,
-                false /*shouldShowComplication*/
+                false, /*shouldShowComplication*/
             )
             // Set communal available, verify that overlay callback is informed.
             kosmos.setCommunalAvailable(true)
@@ -829,7 +829,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -850,7 +850,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -870,7 +870,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            true /*shouldShowComplication*/
+            true, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -894,14 +894,14 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(lifecycleRegistry.mLifecycles)
             .containsExactly(
                 Lifecycle.State.CREATED,
                 Lifecycle.State.STARTED,
-                Lifecycle.State.RESUMED
+                Lifecycle.State.RESUMED,
             )
     }
 
@@ -925,7 +925,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         testScope.runCurrent()
         mMainExecutor.runAllReady()
@@ -942,7 +942,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         mService.onDestroy()
@@ -952,7 +952,7 @@
                 Lifecycle.State.CREATED,
                 Lifecycle.State.STARTED,
                 Lifecycle.State.RESUMED,
-                Lifecycle.State.DESTROYED
+                Lifecycle.State.DESTROYED,
             )
     }
 
@@ -966,7 +966,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED)
@@ -998,7 +998,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED)
@@ -1030,7 +1030,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED)
@@ -1066,7 +1066,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED)
@@ -1101,7 +1101,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -1130,7 +1130,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             true /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -1148,7 +1148,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -1181,7 +1181,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
 
@@ -1214,7 +1214,7 @@
             mDreamComplicationComponentFactory,
             mDreamOverlayComponentFactory,
             mComplicationComponentFactory,
-            mAmbientTouchComponentFactory
+            mAmbientTouchComponentFactory,
         )
 
         mService.onEndDream()
@@ -1223,7 +1223,7 @@
             mDreamComplicationComponentFactory,
             mDreamOverlayComponentFactory,
             mComplicationComponentFactory,
-            mAmbientTouchComponentFactory
+            mAmbientTouchComponentFactory,
         )
 
         client.startDream(
@@ -1231,7 +1231,7 @@
             mDreamOverlayCallback,
             DREAM_COMPONENT,
             false /*isPreview*/,
-            false /*shouldShowComplication*/
+            false, /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         environmentComponents.verifyNoMoreInteractions()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 6e381ca..0b944f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardSmartspaceInteractor
 import com.android.systemui.keyguard.shared.model.ClockSize
+import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
@@ -120,6 +121,7 @@
                     keyguardSmartspaceViewModel,
                     { keyguardBlueprintInteractor },
                     keyguardRootViewModel,
+                    aodBurnInViewModel,
                 )
         }
     }
@@ -313,7 +315,7 @@
             referencedIds.contentEquals(
                 intArrayOf(
                     com.android.systemui.shared.R.id.bc_smartspace_view,
-                    R.id.aod_notification_icon_container
+                    R.id.aod_notification_icon_container,
                 )
             )
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/HydratorTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/lifecycle/HydratorTest.kt
index ec6045c..b0e93fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/HydratorTest.kt
@@ -59,11 +59,11 @@
                 Column {
                     Text(
                         "upstreamStateFlow=${viewModel.stateBackedByStateFlow}",
-                        Modifier.testTag("upstreamStateFlow")
+                        Modifier.testTag("upstreamStateFlow"),
                     )
                     Text(
                         "upstreamFlow=${viewModel.stateBackedByFlow}",
-                        Modifier.testTag("upstreamFlow")
+                        Modifier.testTag("upstreamFlow"),
                     )
                 }
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 680df15..dcf32a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -137,7 +137,7 @@
             MediaTestUtils.emptyMediaData.copy(
                 app = PACKAGE,
                 packageName = PACKAGE,
-                token = session.sessionToken
+                token = session.sessionToken,
             )
 
         resumeData = mediaData.copy(token = null, active = false, resumption = true)
@@ -237,7 +237,7 @@
         // Assuming we're registered
         testOnMediaDataLoaded_registersPlaybackListener()
 
-        mediaCallbackCaptor.value.onPlaybackStateChanged(
+        onPlaybackStateChanged(
             PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
         )
         assertThat(mainExecutor.numPending()).isEqualTo(1)
@@ -249,7 +249,7 @@
         // Assuming we have a pending timeout
         testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
 
-        mediaCallbackCaptor.value.onPlaybackStateChanged(
+        onPlaybackStateChanged(
             PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 0f).build()
         )
         assertThat(mainExecutor.numPending()).isEqualTo(0)
@@ -261,7 +261,7 @@
         // Assuming we have a pending timeout
         testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
 
-        mediaCallbackCaptor.value.onPlaybackStateChanged(
+        onPlaybackStateChanged(
             PlaybackState.Builder().setState(PlaybackState.STATE_STOPPED, 0L, 0f).build()
         )
         assertThat(mainExecutor.numPending()).isEqualTo(1)
@@ -435,7 +435,7 @@
 
         // When the playback state changes, and has different actions
         val playingState = PlaybackState.Builder().setActions(PlaybackState.ACTION_PLAY).build()
-        mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+        onPlaybackStateChanged(playingState)
         assertThat(uiExecutor.runAllReady()).isEqualTo(1)
 
         // Then the callback is invoked
@@ -448,7 +448,7 @@
             PlaybackState.CustomAction.Builder(
                     "ACTION_1",
                     "custom action 1",
-                    android.R.drawable.ic_media_ff
+                    android.R.drawable.ic_media_ff,
                 )
                 .build()
         val pausedState =
@@ -463,7 +463,7 @@
             PlaybackState.CustomAction.Builder(
                     "ACTION_2",
                     "custom action 2",
-                    android.R.drawable.ic_media_rew
+                    android.R.drawable.ic_media_rew,
                 )
                 .build()
         val pausedStateTwoActions =
@@ -472,7 +472,7 @@
                 .addCustomAction(customOne)
                 .addCustomAction(customTwo)
                 .build()
-        mediaCallbackCaptor.value.onPlaybackStateChanged(pausedStateTwoActions)
+        onPlaybackStateChanged(pausedStateTwoActions)
         assertThat(uiExecutor.runAllReady()).isEqualTo(1)
 
         // Then the callback is invoked
@@ -485,7 +485,7 @@
         loadMediaDataWithPlaybackState(stateWithActions)
 
         // When the playback state updates with the same actions
-        mediaCallbackCaptor.value.onPlaybackStateChanged(stateWithActions)
+        onPlaybackStateChanged(stateWithActions)
 
         // Then the callback is not invoked again
         verify(stateCallback, never()).invoke(eq(KEY), any())
@@ -512,7 +512,7 @@
                 .setActions(PlaybackState.ACTION_PAUSE)
                 .addCustomAction(customTwo)
                 .build()
-        mediaCallbackCaptor.value.onPlaybackStateChanged(stateTwo)
+        onPlaybackStateChanged(stateTwo)
 
         // Then the callback is not invoked
         verify(stateCallback, never()).invoke(eq(KEY), any())
@@ -544,7 +544,7 @@
         // When the playback state changes to playing
         val playingState =
             PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
-        mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+        onPlaybackStateChanged(playingState)
         uiExecutor.runAllReady()
 
         // Then the callback is invoked
@@ -561,7 +561,7 @@
         // When the playback state is updated, but still not playing
         val playingState =
             PlaybackState.Builder().setState(PlaybackState.STATE_STOPPED, 0L, 0f).build()
-        mediaCallbackCaptor.value.onPlaybackStateChanged(playingState)
+        onPlaybackStateChanged(playingState)
 
         // Then the callback is not invoked
         verify(stateCallback, never()).invoke(eq(KEY), eq(playingState!!))
@@ -571,7 +571,7 @@
     fun testTimeoutCallback_dozedPastTimeout_invokedOnWakeup() {
         // When paused media is loaded
         testOnMediaDataLoaded_registersPlaybackListener()
-        mediaCallbackCaptor.value.onPlaybackStateChanged(
+        onPlaybackStateChanged(
             PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
         )
         verify(statusBarStateController).addCallback(capture(dozingCallbackCaptor))
@@ -597,7 +597,7 @@
         val time = clock.currentTimeMillis()
         clock.setElapsedRealtime(time)
         testOnMediaDataLoaded_registersPlaybackListener()
-        mediaCallbackCaptor.value.onPlaybackStateChanged(
+        onPlaybackStateChanged(
             PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 0f).build()
         )
         verify(statusBarStateController).addCallback(capture(dozingCallbackCaptor))
@@ -706,4 +706,9 @@
         bgExecutor.runAllReady()
         uiExecutor.runAllReady()
     }
+
+    private fun onPlaybackStateChanged(state: PlaybackState) {
+        mediaCallbackCaptor.value.onPlaybackStateChanged(state)
+        bgExecutor.runAllReady()
+    }
 }
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 53f0800..c4f5d62 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
@@ -76,6 +76,7 @@
 import com.android.settingslib.media.InputRouteManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.animation.ActivityTransitionAnimator;
@@ -103,6 +104,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -120,6 +123,8 @@
     private static final int MAX_VOLUME = 1;
     private static final int CURRENT_VOLUME = 0;
     private static final boolean VOLUME_FIXED_TRUE = true;
+    private static final int LATCH_COUNT_DOWN_TIME_IN_SECOND = 5;
+    private static final int LATCH_TIME_OUT_TIME_IN_SECOND = 10;
 
     @Mock
     private DialogTransitionAnimator mDialogTransitionAnimator;
@@ -1339,4 +1344,39 @@
         assertThat(selectedMediaDevices)
                 .containsExactly(selectedOutputMediaDevice, selectedInputMediaDevice);
     }
+
+    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void selectInputDevice() throws InterruptedException {
+        final MediaDevice inputMediaDevice =
+                InputMediaDevice.create(
+                        mContext,
+                        TEST_DEVICE_1_ID,
+                        AudioDeviceInfo.TYPE_BUILTIN_MIC,
+                        MAX_VOLUME,
+                        CURRENT_VOLUME,
+                        VOLUME_FIXED_TRUE);
+        mMediaSwitchingController.connectDevice(inputMediaDevice);
+
+        CountDownLatch latch = new CountDownLatch(LATCH_COUNT_DOWN_TIME_IN_SECOND);
+        var unused = ThreadUtils.postOnBackgroundThread(latch::countDown);
+        latch.await(LATCH_TIME_OUT_TIME_IN_SECOND, TimeUnit.SECONDS);
+
+        verify(mInputRouteManager, atLeastOnce()).selectDevice(inputMediaDevice);
+        verify(mLocalMediaManager, never()).connectDevice(inputMediaDevice);
+    }
+
+    @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
+    @Test
+    public void selectOutputDevice() throws InterruptedException {
+        final MediaDevice outputMediaDevice = mock(MediaDevice.class);
+        mMediaSwitchingController.connectDevice(outputMediaDevice);
+
+        CountDownLatch latch = new CountDownLatch(LATCH_COUNT_DOWN_TIME_IN_SECOND);
+        var unused = ThreadUtils.postOnBackgroundThread(latch::countDown);
+        latch.await(LATCH_TIME_OUT_TIME_IN_SECOND, TimeUnit.SECONDS);
+
+        verify(mInputRouteManager, never()).selectDevice(outputMediaDevice);
+        verify(mLocalMediaManager, atLeastOnce()).connectDevice(outputMediaDevice);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index b86d571..ab846f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -21,6 +21,7 @@
 import android.hardware.input.KeyGestureEvent
 import android.os.UserHandle
 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.view.KeyEvent
@@ -32,6 +33,8 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
+import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -62,8 +65,7 @@
 @RunWith(AndroidJUnit4::class)
 internal class NoteTaskInitializerTest : SysuiTestCase() {
 
-    @get:Rule
-    val setFlagsRule = SetFlagsRule()
+    @get:Rule val setFlagsRule = SetFlagsRule()
 
     @Mock lateinit var commandQueue: CommandQueue
     @Mock lateinit var inputManager: InputManager
@@ -83,10 +85,7 @@
         whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(true)
     }
 
-    private fun createUnderTest(
-        isEnabled: Boolean,
-        bubbles: Bubbles?,
-    ): NoteTaskInitializer =
+    private fun createUnderTest(isEnabled: Boolean, bubbles: Bubbles?): NoteTaskInitializer =
         NoteTaskInitializer(
             controller = controller,
             commandQueue = commandQueue,
@@ -104,7 +103,7 @@
         code: Int,
         downTime: Long = 0L,
         eventTime: Long = 0L,
-        metaState: Int = 0
+        metaState: Int = 0,
     ): KeyEvent = KeyEvent(downTime, eventTime, action, code, 0 /*repeat*/, metaState)
 
     @Test
@@ -113,7 +112,6 @@
 
         createUnderTest(isEnabled = true, bubbles = bubbles).initialize()
 
-        verify(commandQueue).addCallback(any())
         verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
         verify(controller).updateNoteTaskForCurrentUserAndManagedProfiles()
         verify(keyguardMonitor).registerCallback(any())
@@ -125,7 +123,6 @@
 
         createUnderTest(isEnabled = true, bubbles = bubbles).initialize()
 
-        verify(commandQueue).addCallback(any())
         verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
         verify(controller, never()).setNoteTaskShortcutEnabled(any(), any())
         verify(keyguardMonitor).registerCallback(any())
@@ -165,12 +162,13 @@
     }
 
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     fun initialize_handleSystemKey() {
         val expectedKeyEvent =
             createKeyEvent(
                 ACTION_DOWN,
                 KEYCODE_N,
-                metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+                metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
             )
         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
         underTest.initialize()
@@ -183,22 +181,66 @@
 
     @Test
     @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
-    fun initialize_handleKeyGestureEvent() {
-        val gestureEvent = KeyGestureEvent.Builder()
-            .setKeycodes(intArrayOf(KeyEvent.KEYCODE_N))
-            .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
-            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
-            .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
-            .build()
+    fun handlesShortcut_metaCtrlN() {
+        val gestureEvent =
+            KeyGestureEvent.Builder()
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_N))
+                .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
         underTest.initialize()
-        val callback =
-            withArgCaptor { verify(inputManager).registerKeyGestureEventHandler(capture()) }
+        val callback = withArgCaptor {
+            verify(inputManager).registerKeyGestureEventHandler(capture())
+        }
 
         assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue()
 
         executor.runAllReady()
-        verify(controller).showNoteTask(any())
+        verify(controller).showNoteTask(eq(KEYBOARD_SHORTCUT))
+    }
+
+    @Test
+    @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+    fun handlesShortcut_stylusTailButton() {
+        val gestureEvent =
+            KeyGestureEvent.Builder()
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+        underTest.initialize()
+        val callback = withArgCaptor {
+            verify(inputManager).registerKeyGestureEventHandler(capture())
+        }
+
+        assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue()
+
+        executor.runAllReady()
+        verify(controller).showNoteTask(eq(TAIL_BUTTON))
+    }
+
+    @Test
+    @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+    fun ignoresUnrelatedShortcuts() {
+        val gestureEvent =
+            KeyGestureEvent.Builder()
+                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
+                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+                .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+                .build()
+        val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+        underTest.initialize()
+        val callback = withArgCaptor {
+            verify(inputManager).registerKeyGestureEventHandler(capture())
+        }
+
+        assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isFalse()
+
+        executor.runAllReady()
+        verify(controller, never()).showNoteTask(any())
     }
 
     @Test
@@ -249,6 +291,7 @@
     }
 
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     fun tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUp() {
         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
         underTest.initialize()
@@ -267,6 +310,7 @@
     }
 
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     fun tailButtonGestureDetection_doublePress_shouldNotShowNoteTaskTwice() {
         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
         underTest.initialize()
@@ -289,6 +333,7 @@
     }
 
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     fun tailButtonGestureDetection_longPress_shouldNotShowNoteTask() {
         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
         underTest.initialize()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 755adc6..8d060e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -60,14 +60,13 @@
         onSetTiles: (List<TileSpec>) -> Unit,
     ) {
         DefaultEditTileGrid(
-            currentListState = listState,
+            listState = listState,
             otherTiles = listOf(),
             columns = 4,
             modifier = Modifier.fillMaxSize(),
-            onAddTile = { _, _ -> },
             onRemoveTile = {},
             onSetTiles = onSetTiles,
-            onResize = {},
+            onResize = { _, _ -> },
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
new file mode 100644
index 0000000..ee1c0e9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -0,0 +1,194 @@
+/*
+ * 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.qs.panels.ui.compose
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performCustomAccessibilityActionWithLabel
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeLeft
+import androidx.compose.ui.test.swipeRight
+import androidx.compose.ui.text.AnnotatedString
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.SizedTileImpl
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.DefaultEditTileGrid
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.shared.model.TileCategory
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalTestApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ResizingTest : SysuiTestCase() {
+    @get:Rule val composeRule = createComposeRule()
+
+    @Composable
+    private fun EditTileGridUnderTest(
+        listState: EditTileListState,
+        onResize: (TileSpec, Boolean) -> Unit,
+    ) {
+        DefaultEditTileGrid(
+            listState = listState,
+            otherTiles = listOf(),
+            columns = 4,
+            modifier = Modifier.fillMaxSize(),
+            onRemoveTile = {},
+            onSetTiles = {},
+            onResize = onResize,
+        )
+    }
+
+    @Test
+    fun toggleIconTile_shouldBeLarge() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
+        }
+        composeRule.waitForIdle()
+
+        composeRule
+            .onNodeWithContentDescription("tileA")
+            .performCustomAccessibilityActionWithLabel("Toggle size")
+
+        assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2)
+    }
+
+    @Test
+    fun toggleLargeTile_shouldBeIcon() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
+        }
+        composeRule.waitForIdle()
+
+        composeRule
+            .onNodeWithContentDescription("tileD_large")
+            .performCustomAccessibilityActionWithLabel("Toggle size")
+
+        assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1)
+    }
+
+    @Test
+    fun resizedLarge_shouldBeIcon() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
+        }
+        composeRule.waitForIdle()
+
+        composeRule
+            .onNodeWithContentDescription("tileA")
+            .performClick() // Select
+            .performTouchInput { // Resize down
+                swipeRight()
+            }
+        composeRule.waitForIdle()
+
+        assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(1)
+    }
+
+    @Test
+    fun resizedIcon_shouldBeLarge() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) }
+        }
+        composeRule.waitForIdle()
+
+        composeRule
+            .onNodeWithContentDescription("tileD_large")
+            .performClick() // Select
+            .performTouchInput { // Resize down
+                swipeLeft()
+            }
+        composeRule.waitForIdle()
+
+        assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1)
+    }
+
+    companion object {
+        private fun List<SizedTile<EditTileViewModel>>.resize(
+            spec: TileSpec,
+            toIcon: Boolean,
+        ): List<SizedTile<EditTileViewModel>> {
+            return map {
+                if (it.tile.tileSpec == spec) {
+                    SizedTileImpl(it.tile, width = if (toIcon) 1 else 2)
+                } else {
+                    it
+                }
+            }
+        }
+
+        private fun createEditTile(tileSpec: String): SizedTile<EditTileViewModel> {
+            return SizedTileImpl(
+                EditTileViewModel(
+                    tileSpec = TileSpec.create(tileSpec),
+                    icon =
+                        Icon.Resource(
+                            android.R.drawable.star_on,
+                            ContentDescription.Loaded(tileSpec),
+                        ),
+                    label = AnnotatedString(tileSpec),
+                    appName = null,
+                    isCurrent = true,
+                    availableEditActions = emptySet(),
+                    category = TileCategory.UNKNOWN,
+                ),
+                getWidth(tileSpec),
+            )
+        }
+
+        private fun getWidth(tileSpec: String): Int {
+            return if (tileSpec.endsWith("large")) {
+                2
+            } else {
+                1
+            }
+        }
+
+        private val TestEditTiles =
+            listOf(
+                createEditTile("tileA"),
+                createEditTile("tileB"),
+                createEditTile("tileC"),
+                createEditTile("tileD_large"),
+                createEditTile("tileE"),
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index eea02ee..2f8f45c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -887,6 +888,34 @@
     }
 
     @Test
+    public void getActiveAutoSwitchNonDdsSubId_registerCallbackForExistedSubId_notRegister() {
+        mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
+
+        // Adds non DDS subId
+        SubscriptionInfo info = mock(SubscriptionInfo.class);
+        doReturn(SUB_ID2).when(info).getSubscriptionId();
+        doReturn(false).when(info).isOpportunistic();
+        when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
+
+        mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+
+        // 1st time is onStart(), 2nd time is getActiveAutoSwitchNonDdsSubId()
+        verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any());
+        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size() == 2);
+
+        // Adds non DDS subId again
+        doReturn(SUB_ID2).when(info).getSubscriptionId();
+        doReturn(false).when(info).isOpportunistic();
+        when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
+
+        mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+
+        // Does not add due to cached subInfo in mSubIdTelephonyCallbackMap.
+        verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any());
+        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size() == 2);
+    }
+
+    @Test
     public void getMobileNetworkSummary() {
         mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
         Resources res1 = mock(Resources.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
index 98260d8..41e2467 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogBuffer
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -51,6 +52,7 @@
     @Mock private lateinit var displayTracker: DisplayTracker
     @Mock private lateinit var displayManager: DisplayManager
     @Mock private lateinit var iVrManager: IVrManager
+    @Mock private lateinit var logger: LogBuffer
 
     private lateinit var testableLooper: TestableLooper
 
@@ -69,10 +71,11 @@
                 displayTracker,
                 displayManager,
                 secureSettings,
+                logger,
                 iVrManager,
                 executor,
                 mock(),
-                Handler(testableLooper.looper)
+                Handler(testableLooper.looper),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index c0444fe..859f84e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -76,6 +76,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
@@ -118,7 +119,7 @@
                 override fun create(
                     lifecycleOwner: LifecycleOwner,
                     touchHandlers: Set<TouchHandler>,
-                    loggingName: String
+                    loggingName: String,
                 ): AmbientTouchComponent =
                     object : AmbientTouchComponent {
                         override fun getTouchMonitor(): TouchMonitor = touchMonitor
@@ -141,7 +142,7 @@
                     kosmos.notificationStackScrollLayoutController,
                     kosmos.keyguardMediaController,
                     kosmos.lockscreenSmartspaceController,
-                    logcatLogBuffer("GlanceableHubContainerControllerTest")
+                    logcatLogBuffer("GlanceableHubContainerControllerTest"),
                 )
         }
         testableLooper = TestableLooper.get(this)
@@ -150,7 +151,7 @@
         overrideResource(R.dimen.communal_top_edge_swipe_region_height, TOP_SWIPE_REGION_WIDTH)
         overrideResource(
             R.dimen.communal_bottom_edge_swipe_region_height,
-            BOTTOM_SWIPE_REGION_WIDTH
+            BOTTOM_SWIPE_REGION_WIDTH,
         )
 
         // Make communal available so that communalInteractor.desiredScene accurately reflects
@@ -188,7 +189,7 @@
                         kosmos.notificationStackScrollLayoutController,
                         kosmos.keyguardMediaController,
                         kosmos.lockscreenSmartspaceController,
-                        logcatLogBuffer("GlanceableHubContainerControllerTest")
+                        logcatLogBuffer("GlanceableHubContainerControllerTest"),
                     )
 
                 // First call succeeds.
@@ -217,7 +218,7 @@
                     kosmos.notificationStackScrollLayoutController,
                     kosmos.keyguardMediaController,
                     kosmos.lockscreenSmartspaceController,
-                    logcatLogBuffer("GlanceableHubContainerControllerTest")
+                    logcatLogBuffer("GlanceableHubContainerControllerTest"),
                 )
 
             assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
@@ -241,7 +242,7 @@
                     kosmos.notificationStackScrollLayoutController,
                     kosmos.keyguardMediaController,
                     kosmos.lockscreenSmartspaceController,
-                    logcatLogBuffer("GlanceableHubContainerControllerTest")
+                    logcatLogBuffer("GlanceableHubContainerControllerTest"),
                 )
 
             // Only initView without attaching a view as we don't want the flows to start collecting
@@ -342,7 +343,7 @@
                         from = KeyguardState.GONE,
                         to = KeyguardState.GLANCEABLE_HUB,
                         value = 1.0f,
-                        transitionState = TransitionState.RUNNING
+                        transitionState = TransitionState.RUNNING,
                     )
                 )
                 testableLooper.processAllMessages()
@@ -449,7 +450,6 @@
     fun gestureExclusionZone_setAfterInit() =
         with(kosmos) {
             testScope.runTest {
-                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
                 goToScene(CommunalScenes.Communal)
 
                 assertThat(containerView.systemGestureExclusionRects)
@@ -458,13 +458,7 @@
                             /* left= */ 0,
                             /* top= */ TOP_SWIPE_REGION_WIDTH,
                             /* right= */ CONTAINER_WIDTH,
-                            /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
-                        ),
-                        Rect(
-                            /* left= */ 0,
-                            /* top= */ 0,
-                            /* right= */ 0,
-                            /* bottom= */ CONTAINER_HEIGHT
+                            /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH,
                         )
                     )
             }
@@ -475,67 +469,14 @@
     fun gestureExclusionZone_setAfterInit_fullSwipe() =
         with(kosmos) {
             testScope.runTest {
-                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
                 goToScene(CommunalScenes.Communal)
 
-                assertThat(containerView.systemGestureExclusionRects)
-                    .containsExactly(
-                        Rect(
-                            /* left= */ 0,
-                            /* top= */ 0,
-                            /* right= */ 0,
-                            /* bottom= */ CONTAINER_HEIGHT
-                        )
-                    )
+                assertThat(containerView.systemGestureExclusionRects).isEmpty()
             }
         }
 
     @Test
     @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
-    fun gestureExclusionZone_setAfterInit_rtl() =
-        with(kosmos) {
-            testScope.runTest {
-                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
-                goToScene(CommunalScenes.Communal)
-
-                assertThat(containerView.systemGestureExclusionRects)
-                    .containsExactly(
-                        Rect(
-                            /* left= */ 0,
-                            /* top= */ TOP_SWIPE_REGION_WIDTH,
-                            /* right= */ CONTAINER_WIDTH,
-                            /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
-                        ),
-                        Rect(
-                            /* left= */ 0,
-                            /* top= */ 0,
-                            /* right= */ CONTAINER_WIDTH,
-                            /* bottom= */ CONTAINER_HEIGHT
-                        )
-                    )
-            }
-        }
-
-    @EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
-    fun gestureExclusionZone_setAfterInit_rtl_fullSwipe() =
-        with(kosmos) {
-            testScope.runTest {
-                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
-                goToScene(CommunalScenes.Communal)
-
-                assertThat(containerView.systemGestureExclusionRects)
-                    .containsExactly(
-                        Rect(
-                            /* left= */ 0,
-                            /* top= */ 0,
-                            /* right= */ CONTAINER_WIDTH,
-                            /* bottom= */ CONTAINER_HEIGHT
-                        )
-                    )
-            }
-        }
-
-    @Test
     fun gestureExclusionZone_unsetWhenShadeOpen() =
         with(kosmos) {
             testScope.runTest {
@@ -554,6 +495,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
     fun gestureExclusionZone_unsetWhenBouncerOpen() =
         with(kosmos) {
             testScope.runTest {
@@ -572,6 +514,7 @@
         }
 
     @Test
+    @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
     fun gestureExclusionZone_unsetWhenHubClosed() =
         with(kosmos) {
             testScope.runTest {
@@ -597,7 +540,7 @@
                 whenever(
                         notificationStackScrollLayoutController.isBelowLastNotification(
                             any(),
-                            any()
+                            any(),
                         )
                     )
                     .thenReturn(false)
@@ -675,7 +618,7 @@
                         from = KeyguardState.GONE,
                         to = KeyguardState.GLANCEABLE_HUB,
                         value = 1.0f,
-                        transitionState = TransitionState.RUNNING
+                        transitionState = TransitionState.RUNNING,
                     )
                 )
                 testableLooper.processAllMessages()
@@ -696,7 +639,7 @@
                 whenever(
                         notificationStackScrollLayoutController.isBelowLastNotification(
                             any(),
-                            any()
+                            any(),
                         )
                     )
                     .thenReturn(true)
@@ -728,7 +671,7 @@
                 whenever(
                         notificationStackScrollLayoutController.isBelowLastNotification(
                             any(),
-                            any()
+                            any(),
                         )
                     )
                     .thenReturn(true)
@@ -752,7 +695,7 @@
                 whenever(
                         notificationStackScrollLayoutController.isBelowLastNotification(
                             any(),
-                            any()
+                            any(),
                         )
                     )
                     .thenReturn(true)
@@ -775,6 +718,15 @@
             }
         }
 
+    @Test
+    fun disposeView_destroysTouchMonitor() {
+        clearInvocations(touchMonitor)
+
+        underTest.disposeView()
+
+        verify(touchMonitor).destroy()
+    }
+
     private fun initAndAttachContainerView() {
         val mockInsets =
             mock<WindowInsets> {
@@ -805,13 +757,13 @@
             kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.GLANCEABLE_HUB,
-                kosmos.testScope
+                kosmos.testScope,
             )
         } else {
             kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.GLANCEABLE_HUB,
                 to = KeyguardState.LOCKSCREEN,
-                kosmos.testScope
+                kosmos.testScope,
             )
         }
         testableLooper.processAllMessages()
@@ -836,7 +788,7 @@
                 MotionEvent.ACTION_DOWN,
                 CONTAINER_WIDTH.toFloat() / 2,
                 CONTAINER_HEIGHT.toFloat() / 2,
-                0
+                0,
             )
 
         private val CANCEL_EVENT =
@@ -846,7 +798,7 @@
                 MotionEvent.ACTION_CANCEL,
                 CONTAINER_WIDTH.toFloat() / 2,
                 CONTAINER_HEIGHT.toFloat() / 2,
-                0
+                0,
             )
 
         private val MOVE_EVENT =
@@ -856,7 +808,7 @@
                 MotionEvent.ACTION_MOVE,
                 CONTAINER_WIDTH.toFloat() / 2,
                 CONTAINER_HEIGHT.toFloat() / 2,
-                0
+                0,
             )
 
         private val UP_EVENT =
@@ -866,7 +818,7 @@
                 MotionEvent.ACTION_UP,
                 CONTAINER_WIDTH.toFloat() / 2,
                 CONTAINER_HEIGHT.toFloat() / 2,
-                0
+                0,
             )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 4bd0c75..a6afd0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -453,6 +453,7 @@
                 mUiEventLogger,
                 () -> mKosmos.getInteractionJankMonitor(),
                 mJavaAdapter,
+                () -> mKeyguardInteractor,
                 () -> mKeyguardTransitionInteractor,
                 () -> mShadeInteractor,
                 () -> mKosmos.getDeviceUnlockedInteractor(),
@@ -611,6 +612,7 @@
                                 new UiEventLoggerFake(),
                                 () -> mKosmos.getInteractionJankMonitor(),
                                 mJavaAdapter,
+                                () -> mKeyguardInteractor,
                                 () -> mKeyguardTransitionInteractor,
                                 () -> mShadeInteractor,
                                 () -> mKosmos.getDeviceUnlockedInteractor(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index a1750cd..b1ec740 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
@@ -28,6 +29,7 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.setSceneTransition
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
@@ -38,9 +40,9 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -55,14 +57,9 @@
     private val configurationRepository = kosmos.fakeConfigurationRepository
     private val keyguardRepository = kosmos.fakeKeyguardRepository
     private val sceneInteractor = kosmos.sceneInteractor
-    private val shadeTestUtil = kosmos.shadeTestUtil
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
 
-    private lateinit var underTest: ShadeInteractorSceneContainerImpl
-
-    @Before
-    fun setUp() {
-        underTest = kosmos.shadeInteractorSceneContainerImpl
-    }
+    private val underTest by lazy { kosmos.shadeInteractorSceneContainerImpl }
 
     @Test
     fun qsExpansionWhenInSplitShadeAndQsExpanded() =
@@ -600,14 +597,14 @@
 
     @Test
     @EnableFlags(DualShade.FLAG_NAME)
-    fun expandNotificationShade_dualShadeEnabled_opensOverlay() =
+    fun expandNotificationsShade_dualShade_opensOverlay() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
 
-            underTest.expandNotificationShade("reason")
+            underTest.expandNotificationsShade("reason")
 
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade)
@@ -615,14 +612,15 @@
 
     @Test
     @DisableFlags(DualShade.FLAG_NAME)
-    fun expandNotificationShade_dualShadeDisabled_switchesToShadeScene() =
+    fun expandNotificationsShade_singleShade_switchesToShadeScene() =
         testScope.runTest {
+            shadeTestUtil.setSplitShade(false)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).isEmpty()
 
-            underTest.expandNotificationShade("reason")
+            underTest.expandNotificationsShade("reason")
 
             assertThat(currentScene).isEqualTo(Scenes.Shade)
             assertThat(currentOverlays).isEmpty()
@@ -630,7 +628,7 @@
 
     @Test
     @EnableFlags(DualShade.FLAG_NAME)
-    fun expandNotificationShade_dualShadeEnabledAndQuickSettingsOpen_replacesOverlay() =
+    fun expandNotificationsShade_dualShadeQuickSettingsOpen_replacesOverlay() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
@@ -638,14 +636,14 @@
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade)
 
-            underTest.expandNotificationShade("reason")
+            underTest.expandNotificationsShade("reason")
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade)
         }
 
     @Test
     @EnableFlags(DualShade.FLAG_NAME)
-    fun expandQuickSettingsShade_dualShadeEnabled_opensOverlay() =
+    fun expandQuickSettingsShade_dualShade_opensOverlay() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
@@ -660,8 +658,9 @@
 
     @Test
     @DisableFlags(DualShade.FLAG_NAME)
-    fun expandQuickSettingsShade_dualShadeDisabled_switchesToQuickSettingsScene() =
+    fun expandQuickSettingsShade_singleShade_switchesToQuickSettingsScene() =
         testScope.runTest {
+            shadeTestUtil.setSplitShade(false)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -674,12 +673,28 @@
         }
 
     @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun expandQuickSettingsShade_splitShade_switchesToShadeScene() =
+        testScope.runTest {
+            shadeTestUtil.setSplitShade(true)
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+
+            underTest.expandQuickSettingsShade("reason")
+
+            assertThat(currentScene).isEqualTo(Scenes.Shade)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    @Test
     @EnableFlags(DualShade.FLAG_NAME)
-    fun expandQuickSettingsShade_dualShadeEnabledAndNotificationsOpen_replacesOverlay() =
+    fun expandQuickSettingsShade_dualShadeNotificationsOpen_replacesOverlay() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
-            underTest.expandNotificationShade("reason")
+            underTest.expandNotificationsShade("reason")
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).containsExactly(Overlays.NotificationsShade)
 
@@ -687,4 +702,141 @@
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
             assertThat(currentOverlays).containsExactly(Overlays.QuickSettingsShade)
         }
+
+    @Test
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun collapseNotificationsShade_dualShade_hidesOverlay() =
+        testScope.runTest {
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            openShade(Overlays.NotificationsShade)
+
+            underTest.collapseNotificationsShade("reason")
+
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun collapseNotificationsShade_singleShade_switchesToLockscreen() =
+        testScope.runTest {
+            shadeTestUtil.setSplitShade(false)
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            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 {
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            openShade(Overlays.QuickSettingsShade)
+
+            underTest.collapseQuickSettingsShade("reason")
+
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun collapseQuickSettingsShadeNotBypassingShade_singleShade_switchesToShade() =
+        testScope.runTest {
+            shadeTestUtil.setSplitShade(false)
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+            assertThat(currentOverlays).isEmpty()
+
+            underTest.collapseQuickSettingsShade(
+                loggingReason = "reason",
+                bypassNotificationsShade = false,
+            )
+
+            assertThat(currentScene).isEqualTo(Scenes.Shade)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun collapseQuickSettingsShadeNotBypassingShade_splitShade_switchesToLockscreen() =
+        testScope.runTest {
+            shadeTestUtil.setSplitShade(true)
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+            assertThat(currentOverlays).isEmpty()
+
+            underTest.collapseQuickSettingsShade(
+                loggingReason = "reason",
+                bypassNotificationsShade = false,
+            )
+
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    @Test
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun collapseQuickSettingsShadeBypassingShade_singleShade_switchesToLockscreen() =
+        testScope.runTest {
+            shadeTestUtil.setSplitShade(false)
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+            assertThat(currentOverlays).isEmpty()
+
+            underTest.collapseQuickSettingsShade(
+                loggingReason = "reason",
+                bypassNotificationsShade = true,
+            )
+
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    @Test
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun collapseEitherShade_dualShade_hidesBothOverlays() =
+        testScope.runTest {
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            openShade(Overlays.QuickSettingsShade)
+            openShade(Overlays.NotificationsShade)
+            assertThat(currentOverlays)
+                .containsExactly(Overlays.QuickSettingsShade, Overlays.NotificationsShade)
+
+            underTest.collapseEitherShade("reason")
+
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+        }
+
+    private fun TestScope.openShade(overlay: OverlayKey) {
+        val isAnyExpanded by collectLastValue(underTest.isAnyExpanded)
+        val currentScene by collectLastValue(sceneInteractor.currentScene)
+        val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+        val initialScene = checkNotNull(currentScene)
+        sceneInteractor.showOverlay(overlay, "reason")
+        kosmos.setSceneTransition(
+            ObservableTransitionState.Idle(initialScene, checkNotNull(currentOverlays))
+        )
+        runCurrent()
+        assertThat(currentScene).isEqualTo(initialScene)
+        assertThat(currentOverlays).contains(overlay)
+        assertThat(isAnyExpanded).isTrue()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 7ddf7a3..93ba8e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -35,7 +35,8 @@
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginLifecycleManager;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.PluginWrapper;
+import com.android.systemui.plugins.TestPlugin;
 import com.android.systemui.plugins.annotations.Requires;
 
 import org.junit.Before;
@@ -60,7 +61,7 @@
 
     private FakeListener mPluginListener;
     private VersionInfo mVersionInfo;
-    private VersionInfo.InvalidVersionException mVersionException;
+    private boolean mVersionCheckResult = true;
     private PluginInstance.VersionChecker mVersionChecker;
 
     private RefCounter mCounter;
@@ -83,14 +84,16 @@
         mVersionInfo = new VersionInfo();
         mVersionChecker = new PluginInstance.VersionChecker() {
             @Override
-            public <T extends Plugin> VersionInfo checkVersion(
+            public <T extends Plugin> boolean checkVersion(
                     Class<T> instanceClass,
                     Class<T> pluginClass,
                     Plugin plugin
             ) {
-                if (mVersionException != null) {
-                    throw mVersionException;
-                }
+                return mVersionCheckResult;
+            }
+
+            @Override
+            public <T extends Plugin> VersionInfo getVersionInfo(Class<T> instanceClass) {
                 return mVersionInfo;
             }
         };
@@ -117,21 +120,29 @@
     }
 
     @Test
-    public void testCorrectVersion() {
-        assertNotNull(mPluginInstance);
+    public void testCorrectVersion_onCreateBuildsPlugin() {
+        mVersionCheckResult = true;
+        assertFalse(mPluginInstance.hasError());
+
+        mPluginInstance.onCreate();
+        assertFalse(mPluginInstance.hasError());
+        assertNotNull(mPluginInstance.getPlugin());
     }
 
-    @Test(expected = VersionInfo.InvalidVersionException.class)
-    public void testIncorrectVersion() throws Exception {
+    @Test
+    public void testIncorrectVersion_destroysPluginInstance() throws Exception {
         ComponentName wrongVersionTestPluginComponentName =
                 new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName());
 
-        mVersionException = new VersionInfo.InvalidVersionException("test", true);
+        mVersionCheckResult = false;
+        assertFalse(mPluginInstance.hasError());
 
         mPluginInstanceFactory.create(
                 mContext, mAppInfo, wrongVersionTestPluginComponentName,
                 TestPlugin.class, mPluginListener);
         mPluginInstance.onCreate();
+        assertTrue(mPluginInstance.hasError());
+        assertNull(mPluginInstance.getPlugin());
     }
 
     @Test
@@ -139,7 +150,7 @@
         mPluginInstance.onCreate();
         assertEquals(1, mPluginListener.mAttachedCount);
         assertEquals(1, mPluginListener.mLoadCount);
-        assertEquals(mPlugin.get(), mPluginInstance.getPlugin());
+        assertEquals(mPlugin.get(), unwrap(mPluginInstance.getPlugin()));
         assertInstances(1, 1);
     }
 
@@ -176,6 +187,17 @@
     }
 
     @Test
+    public void testLinkageError_caughtAndPluginDestroyed() {
+        mPluginInstance.onCreate();
+        assertFalse(mPluginInstance.hasError());
+
+        Object result = mPluginInstance.getPlugin().methodThrowsError();
+        assertNotNull(result);  // Wrapper function should return non-null;
+        assertTrue(mPluginInstance.hasError());
+        assertNull(mPluginInstance.getPlugin());
+    }
+
+    @Test
     public void testLoadUnloadSimultaneous_HoldsUnload() throws Throwable {
         final Semaphore loadLock = new Semaphore(1);
         final Semaphore unloadLock = new Semaphore(1);
@@ -232,6 +254,13 @@
         assertNull(mPluginInstance.getPlugin());
     }
 
+    private static <T> T unwrap(T plugin) {
+        if (plugin instanceof PluginWrapper) {
+            return ((PluginWrapper<T>) plugin).getPlugin();
+        }
+        return plugin;
+    }
+
     private boolean getLock(Semaphore lock, long millis) {
         try {
             return lock.tryAcquire(millis, TimeUnit.MILLISECONDS);
@@ -243,14 +272,6 @@
         }
     }
 
-    // This target class doesn't matter, it just needs to have a Requires to hit the flow where
-    // the mock version info is called.
-    @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
-    public interface TestPlugin extends Plugin {
-        int VERSION = 1;
-        String ACTION = "testAction";
-    }
-
     private void assertInstances(int allocated, int created) {
         // If there are more than the expected number of allocated instances, then we run the
         // garbage collector to finalize and deallocate any outstanding non-referenced instances.
@@ -300,6 +321,11 @@
         public void onDestroy() {
             mCounter.mCreatedInstances.getAndDecrement();
         }
+
+        @Override
+        public Object methodThrowsError() {
+            throw new LinkageError();
+        }
     }
 
     public class FakeListener implements PluginListener<TestPlugin> {
@@ -337,7 +363,7 @@
             mLoadCount++;
             TestPlugin expectedPlugin = PluginInstanceTest.this.mPlugin.get();
             if (expectedPlugin != null) {
-                assertEquals(expectedPlugin, plugin);
+                assertEquals(expectedPlugin, unwrap(plugin));
             }
             Context expectedContext = PluginInstanceTest.this.mPluginContext.get();
             if (expectedContext != null) {
@@ -357,7 +383,7 @@
             mUnloadCount++;
             TestPlugin expectedPlugin = PluginInstanceTest.this.mPlugin.get();
             if (expectedPlugin != null) {
-                assertEquals(expectedPlugin, plugin);
+                assertEquals(expectedPlugin, unwrap(plugin));
             }
             assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
             if (mOnUnload != null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/QuickStepContractTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/system/QuickStepContractTest.kt
new file mode 100644
index 0000000..6254fb1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/QuickStepContractTest.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.system
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_COMMUNAL_HUB_SHOWING
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QuickStepContractTest : SysuiTestCase() {
+    @Test
+    fun isBackGestureDisabled_hubShowing() {
+        val sysuiStateFlags = SYSUI_STATE_COMMUNAL_HUB_SHOWING
+
+        // Gestures are disabled while on the hub.
+        assertThat(
+                QuickStepContract.isBackGestureDisabled(sysuiStateFlags, /* forTrackpad= */ false)
+            )
+            .isTrue()
+    }
+
+    @Test
+    fun isBackGestureDisabled_hubAndShadeShowing() {
+        val sysuiStateFlags =
+            SYSUI_STATE_COMMUNAL_HUB_SHOWING and SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
+
+        // Gestures are enabled because the shade shows over the hub.
+        assertThat(
+                QuickStepContract.isBackGestureDisabled(sysuiStateFlags, /* forTrackpad= */ false)
+            )
+            .isFalse()
+    }
+
+    @Test
+    fun isBackGestureDisabled_hubAndBouncerShowing() {
+        val sysuiStateFlags = SYSUI_STATE_COMMUNAL_HUB_SHOWING and SYSUI_STATE_BOUNCER_SHOWING
+
+        // Gestures are enabled because the bouncer shows over the hub.
+        assertThat(
+                QuickStepContract.isBackGestureDisabled(sysuiStateFlags, /* forTrackpad= */ false)
+            )
+            .isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS
index 1c52b8d..48a20dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS
@@ -1,3 +1,4 @@
 set noparent
 
 include /packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+include /packages/SystemUI/TEST_OWNERS
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
new file mode 100644
index 0000000..9142972
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.core
+
+import android.app.FragmentManager
+import android.app.FragmentTransaction
+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
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarInitializerTest : SysuiTestCase() {
+    val windowController = mock(StatusBarWindowController::class.java)
+
+    @Before
+    fun setup() {
+        // TODO(b/364360986) this will go away once the fragment is deprecated. Hence, there is no
+        // need right now for moving this to kosmos
+        val transaction = mock(FragmentTransaction::class.java)
+        val fragmentManager = mock(FragmentManager::class.java)
+        val fragmentHostManager = mock(FragmentHostManager::class.java)
+        whenever(fragmentHostManager.addTagListener(any(), any())).thenReturn(fragmentHostManager)
+        whenever(fragmentHostManager.fragmentManager).thenReturn(fragmentManager)
+        whenever(fragmentManager.beginTransaction()).thenReturn(transaction)
+        whenever(transaction.replace(any(), any(), any())).thenReturn(transaction)
+
+        whenever(windowController.fragmentHostManager).thenReturn(fragmentHostManager)
+    }
+
+    val underTest =
+        StatusBarInitializerImpl(
+            windowController,
+            { mock(CollapsedStatusBarFragment::class.java) },
+            setOf(),
+        )
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+    fun simpleFragment_startsFromCoreStartable() {
+        underTest.start()
+        assertThat(underTest.initialized).isTrue()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+    fun simpleFragment_throwsIfInitializeIsCalled() {
+        assertThrows(IllegalStateException::class.java) { underTest.initializeStatusBar() }
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+    fun flagOff_doesNotInitializeViaCoreStartable() {
+        underTest.start()
+        assertThat(underTest.initialized).isFalse()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+    fun flagOff_doesNotThrowIfInitializeIsCalled() {
+        underTest.initializeStatusBar()
+        assertThat(underTest.initialized).isTrue()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
index d2dfc92..907c684 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
@@ -17,16 +17,19 @@
 import android.app.StatusBarManager.DISABLE2_NONE
 import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
 import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
 import android.app.StatusBarManager.DISABLE_CLOCK
 import android.app.StatusBarManager.DISABLE_NONE
 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
+import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
 import android.content.res.Configuration
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
@@ -82,7 +85,7 @@
     @Test
     fun disableFlags_initialValue_none() {
         assertThat(underTest.disableFlags.value)
-            .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE))
+            .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = false))
     }
 
     @Test
@@ -182,12 +185,7 @@
     fun disableFlags_quickSettingsDisabled_quickSettingsEnabledFalse() =
         testScope.runTest {
             getCommandQueueCallback()
-                .disable(
-                    DISPLAY_ID,
-                    DISABLE_NONE,
-                    DISABLE2_QUICK_SETTINGS,
-                    /* animate= */ false,
-                )
+                .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_QUICK_SETTINGS, /* animate= */ false)
 
             assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
         }
@@ -217,21 +215,84 @@
             configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
             mContext.orCreateTestableResources.addOverride(
                 R.bool.config_use_split_notification_shade,
-                /* value= */ false
+                /* value= */ false,
             )
             remoteInputQuickSettingsDisabler.setRemoteInputActive(true)
             remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
 
             getCommandQueueCallback()
+                .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
+
+            // THEN quick settings is disabled (even if the disable flags don't say so)
+            assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
+        }
+
+    @Test
+    fun disableFlags_clockDisabled() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(DISPLAY_ID, DISABLE_CLOCK, DISABLE2_NONE, /* animate= */ false)
+
+            assertThat(underTest.disableFlags.value.isClockEnabled).isFalse()
+        }
+
+    @Test
+    fun disableFlags_clockEnabled() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
+
+            assertThat(underTest.disableFlags.value.isClockEnabled).isTrue()
+        }
+
+    @Test
+    fun disableFlags_notificationIconsDisabled() =
+        testScope.runTest {
+            getCommandQueueCallback()
                 .disable(
                     DISPLAY_ID,
-                    DISABLE_NONE,
+                    DISABLE_NOTIFICATION_ICONS,
                     DISABLE2_NONE,
                     /* animate= */ false,
                 )
 
-            // THEN quick settings is disabled (even if the disable flags don't say so)
-            assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
+            assertThat(underTest.disableFlags.value.areNotificationIconsEnabled).isFalse()
+        }
+
+    @Test
+    fun disableFlags_notificationIconsEnabled() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
+
+            assertThat(underTest.disableFlags.value.areNotificationIconsEnabled).isTrue()
+        }
+
+    @Test
+    fun disableFlags_systemInfoDisabled_viaDisable1() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(DISPLAY_ID, DISABLE_SYSTEM_INFO, DISABLE2_NONE, /* animate= */ false)
+
+            assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isFalse()
+        }
+
+    @Test
+    fun disableFlags_systemInfoDisabled_viaDisable2() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_SYSTEM_ICONS, /* animate= */ false)
+
+            assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isFalse()
+        }
+
+    @Test
+    fun disableFlags_systemInfoEnabled() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
+
+            assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isTrue()
         }
 
     @Test
@@ -267,6 +328,34 @@
             assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
         }
 
+    @Test
+    fun disableFlags_animateFalse() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(
+                    DISPLAY_ID,
+                    DISABLE_NOTIFICATION_ALERTS,
+                    DISABLE2_NONE,
+                    /* animate= */ false,
+                )
+
+            assertThat(underTest.disableFlags.value.animate).isFalse()
+        }
+
+    @Test
+    fun disableFlags_animateTrue() =
+        testScope.runTest {
+            getCommandQueueCallback()
+                .disable(
+                    DISPLAY_ID,
+                    DISABLE_NOTIFICATION_ALERTS,
+                    DISABLE2_NONE,
+                    /* animate= */ true,
+                )
+
+            assertThat(underTest.disableFlags.value.animate).isTrue()
+        }
+
     private fun getCommandQueueCallback(): CommandQueue.Callbacks {
         val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
         verify(commandQueue).addCallback(callbackCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index 0407fc1..ac73882 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -24,7 +24,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
 import com.android.systemui.util.DeviceConfigProxyFake
 import com.android.systemui.util.Utils
@@ -42,7 +42,7 @@
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 // this class has no testable logic with either of these flags enabled
-@DisableFlags(PriorityPeopleSection.FLAG_NAME, NotificationMinimalismPrototype.FLAG_NAME)
+@DisableFlags(PriorityPeopleSection.FLAG_NAME, NotificationMinimalism.FLAG_NAME)
 class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
     lateinit var manager: NotificationSectionsFeatureManager
     private val proxyFake = DeviceConfigProxyFake()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS
index 7f5384d..1da7007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS
@@ -1,3 +1,4 @@
 set noparent
 
-include /packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
\ No newline at end of file
+include /packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
+include /packages/SystemUI/TEST_OWNERS
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
index 07c29a0..0c65c9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
@@ -20,7 +20,6 @@
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
@@ -30,15 +29,12 @@
 
 import android.app.Notification.MediaStyle;
 import android.media.session.MediaSession;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
 import android.service.notification.NotificationListenerService;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
 import com.android.systemui.statusbar.notification.InflationException;
@@ -158,8 +154,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_ICONS)
-    public void inflateMediaNotificationIconsMediaEnabled_old() throws InflationException {
+    public void inflateMediaNotificationIconsMediaEnabled() throws InflationException {
         finishSetupWithMediaFeatureFlagEnabled(true);
 
         mListener.onEntryInit(mMediaEntry);
@@ -187,37 +182,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_ICONS)
-    public void inflateMediaNotificationIconsMediaEnabled_new() throws InflationException {
-        finishSetupWithMediaFeatureFlagEnabled(true);
-
-        mListener.onEntryInit(mMediaEntry);
-        mListener.onEntryAdded(mMediaEntry);
-        verify(mIconManager).createIcons(eq(mMediaEntry));
-        verify(mIconManager, never()).updateIcons(eq(mMediaEntry), anyBoolean());
-        clearInvocations(mIconManager);
-
-        mFilter.shouldFilterOut(mMediaEntry, 0);
-        verify(mIconManager, never()).createIcons(eq(mMediaEntry));
-        verify(mIconManager, never()).updateIcons(eq(mMediaEntry), anyBoolean());
-
-        mListener.onEntryUpdated(mMediaEntry);
-        verify(mIconManager, never()).createIcons(eq(mMediaEntry));
-        verify(mIconManager).updateIcons(eq(mMediaEntry), /* usingCache = */ eq(false));
-
-        mListener.onEntryRemoved(mMediaEntry, NotificationListenerService.REASON_CANCEL);
-        mListener.onEntryCleanUp(mMediaEntry);
-        clearInvocations(mIconManager);
-
-        mListener.onEntryInit(mMediaEntry);
-        mListener.onEntryAdded(mMediaEntry);
-        verify(mIconManager).createIcons(eq(mMediaEntry));
-        verify(mIconManager, never()).updateIcons(eq(mMediaEntry), anyBoolean());
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_ICONS)
-    public void inflationException_old() throws InflationException {
+    public void inflationException() throws InflationException {
         finishSetupWithMediaFeatureFlagEnabled(true);
 
         mListener.onEntryInit(mMediaEntry);
@@ -244,31 +209,6 @@
         verify(mIconManager, never()).updateIcons(eq(mMediaEntry), anyBoolean());
     }
 
-    @Test
-    @EnableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_ICONS)
-    public void inflationException_new() throws InflationException {
-        finishSetupWithMediaFeatureFlagEnabled(true);
-
-        doThrow(InflationException.class).when(mIconManager).createIcons(eq(mMediaEntry));
-
-        mListener.onEntryInit(mMediaEntry);
-        mListener.onEntryAdded(mMediaEntry);
-        verify(mIconManager).createIcons(eq(mMediaEntry));
-        verify(mIconManager, never()).updateIcons(eq(mMediaEntry), anyBoolean());
-        clearInvocations(mIconManager);
-
-        mListener.onEntryUpdated(mMediaEntry);
-        verify(mIconManager).createIcons(eq(mMediaEntry));
-        verify(mIconManager, never()).updateIcons(eq(mMediaEntry), anyBoolean());
-        clearInvocations(mIconManager);
-
-        doNothing().when(mIconManager).createIcons(eq(mMediaEntry));
-
-        mListener.onEntryUpdated(mMediaEntry);
-        verify(mIconManager).createIcons(eq(mMediaEntry));
-        verify(mIconManager, never()).updateIcons(eq(mMediaEntry), anyBoolean());
-    }
-
     private void finishSetupWithMediaFeatureFlagEnabled(boolean mediaFeatureFlagEnabled) {
         when(mMediaFeatureFlag.getEnabled()).thenReturn(mediaFeatureFlagEnabled);
         mCoordinator = new MediaCoordinator(mMediaFeatureFlag, mStatusBarService, mIconManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
index deb3fc1..a3f8452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
@@ -15,8 +15,8 @@
  */
 package com.android.systemui.statusbar.notification.collection.coordinator
 
-import android.app.Flags.lifetimeExtensionRefactor
 import android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR
+import android.app.Flags.lifetimeExtensionRefactor
 import android.app.Notification
 import android.app.RemoteInputHistoryItem
 import android.os.Handler
@@ -47,10 +47,10 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations.initMocks
 
 @SmallTest
@@ -78,21 +78,20 @@
     @Before
     fun setUp() {
         initMocks(this)
-        coordinator = RemoteInputCoordinator(
+        coordinator =
+            RemoteInputCoordinator(
                 dumpManager,
                 rebuilder,
                 remoteInputManager,
                 mainHandler,
-                smartReplyController
-        )
+                smartReplyController,
+            )
         `when`(pipeline.addNotificationLifetimeExtender(any())).thenAnswer {
             (it.arguments[0] as NotifLifetimeExtender).setCallback(lifetimeExtensionCallback)
         }
         `when`(pipeline.getInternalNotifUpdater(any())).thenReturn(notifUpdater)
         coordinator.attach(pipeline)
-        listener = withArgCaptor {
-            verify(remoteInputManager).setRemoteInputListener(capture())
-        }
+        listener = withArgCaptor { verify(remoteInputManager).setRemoteInputListener(capture()) }
         entry1 = NotificationEntryBuilder().setId(1).build()
         entry2 = NotificationEntryBuilder().setId(2).build()
         `when`(rebuilder.rebuildForCanceledSmartReplies(any())).thenReturn(sbn)
@@ -101,13 +100,17 @@
         `when`(rebuilder.rebuildWithExistingReplies(any())).thenReturn(sbn)
     }
 
-    val remoteInputActiveExtender get() = coordinator.mRemoteInputActiveExtender
-    val remoteInputHistoryExtender get() = coordinator.mRemoteInputHistoryExtender
-    val smartReplyHistoryExtender get() = coordinator.mSmartReplyHistoryExtender
+    val remoteInputActiveExtender
+        get() = coordinator.mRemoteInputActiveExtender
 
-    val collectionListeners get() = captureMany {
-        verify(pipeline, times(1)).addCollectionListener(capture())
-    }
+    val remoteInputHistoryExtender
+        get() = coordinator.mRemoteInputHistoryExtender
+
+    val smartReplyHistoryExtender
+        get() = coordinator.mSmartReplyHistoryExtender
+
+    val collectionListeners
+        get() = captureMany { verify(pipeline, times(1)).addCollectionListener(capture()) }
 
     @Test
     fun testRemoteInputActive() {
@@ -179,7 +182,8 @@
     @EnableFlags(FLAG_LIFETIME_EXTENSION_REFACTOR)
     fun testRemoteInputLifetimeExtensionListenerTrigger() {
         // Create notification with LIFETIME_EXTENDED_BY_DIRECT_REPLY flag.
-        val entry = NotificationEntryBuilder()
+        val entry =
+            NotificationEntryBuilder()
                 .setId(3)
                 .setTag("entry")
                 .setFlag(mContext, Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true)
@@ -187,9 +191,7 @@
         `when`(remoteInputManager.shouldKeepForRemoteInputHistory(entry)).thenReturn(true)
         `when`(remoteInputManager.shouldKeepForSmartReplyHistory(entry)).thenReturn(false)
 
-        collectionListeners.forEach {
-            it.onEntryUpdated(entry, true)
-        }
+        collectionListeners.forEach { it.onEntryUpdated(entry, true) }
 
         verify(rebuilder, times(1)).rebuildForRemoteInputReply(entry)
     }
@@ -198,16 +200,15 @@
     @EnableFlags(FLAG_LIFETIME_EXTENSION_REFACTOR)
     fun testSmartReplyLifetimeExtensionListenerTrigger() {
         // Create notification with LIFETIME_EXTENDED_BY_DIRECT_REPLY flag.
-        val entry = NotificationEntryBuilder()
+        val entry =
+            NotificationEntryBuilder()
                 .setId(3)
                 .setTag("entry")
                 .setFlag(mContext, Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true)
                 .build()
         `when`(remoteInputManager.shouldKeepForRemoteInputHistory(entry)).thenReturn(false)
         `when`(remoteInputManager.shouldKeepForSmartReplyHistory(entry)).thenReturn(true)
-        collectionListeners.forEach {
-            it.onEntryUpdated(entry, true)
-        }
+        collectionListeners.forEach { it.onEntryUpdated(entry, true) }
 
         verify(rebuilder, times(1)).rebuildForCanceledSmartReplies(entry)
         verify(smartReplyController, times(1)).stopSending(entry)
@@ -217,25 +218,25 @@
     @EnableFlags(FLAG_LIFETIME_EXTENSION_REFACTOR)
     fun testRepeatedUpdateTriggersRebuild() {
         // Create notification with LIFETIME_EXTENDED_BY_DIRECT_REPLY flag.
-        val entry = NotificationEntryBuilder()
+        val entry =
+            NotificationEntryBuilder()
                 .setId(3)
                 .setTag("entry")
                 .setFlag(mContext, Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true)
                 .build()
         `when`(remoteInputManager.shouldKeepForRemoteInputHistory(entry)).thenReturn(false)
         `when`(remoteInputManager.shouldKeepForSmartReplyHistory(entry)).thenReturn(false)
-        collectionListeners.forEach {
-            it.onEntryUpdated(entry, true)
-        }
+        collectionListeners.forEach { it.onEntryUpdated(entry, true) }
 
-        verify(rebuilder, times(1)).rebuildWithExistingReplies(entry)
+        verify(rebuilder, times(1)).rebuildForRemoteInputReply(entry)
     }
 
     @Test
     @EnableFlags(FLAG_LIFETIME_EXTENSION_REFACTOR)
     fun testLifetimeExtensionListenerClearsRemoteInputs() {
         // Create notification with LIFETIME_EXTENDED_BY_DIRECT_REPLY flag.
-        val entry = NotificationEntryBuilder()
+        val entry =
+            NotificationEntryBuilder()
                 .setId(3)
                 .setTag("entry")
                 .setFlag(mContext, Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, false)
@@ -245,9 +246,7 @@
         `when`(remoteInputManager.shouldKeepForRemoteInputHistory(entry)).thenReturn(false)
         `when`(remoteInputManager.shouldKeepForSmartReplyHistory(entry)).thenReturn(false)
 
-        collectionListeners.forEach {
-            it.onEntryUpdated(entry, true)
-        }
+        collectionListeners.forEach { it.onEntryUpdated(entry, true) }
 
         assertThat(entry.remoteInputs).isNull()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 83ad18b..46f3a6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.ui.isAnimating
@@ -254,6 +255,39 @@
             assertThat(buttonLabel).isEqualTo(R.string.manage_notifications_history_text)
         }
 
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @Test
+    fun manageButtonOnClick_whenHistoryDisabled() =
+        testScope.runTest {
+            val onClick by collectLastValue(underTest.manageOrHistoryButtonClick)
+            runCurrent()
+
+            // WHEN notification history is disabled
+            fakeSecureSettingsRepository.setInt(Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0)
+
+            // THEN onClick leads to settings page
+            assertThat(onClick?.targetIntent?.action)
+                .isEqualTo(Settings.ACTION_NOTIFICATION_SETTINGS)
+            assertThat(onClick?.backStack).isEmpty()
+        }
+
+    @EnableFlags(ModesEmptyShadeFix.FLAG_NAME)
+    @Test
+    fun historyButtonOnClick_whenHistoryEnabled() =
+        testScope.runTest {
+            val onClick by collectLastValue(underTest.manageOrHistoryButtonClick)
+            runCurrent()
+
+            // WHEN notification history is enabled
+            fakeSecureSettingsRepository.setInt(Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 1)
+
+            // THEN onClick leads to history page
+            assertThat(onClick?.targetIntent?.action)
+                .isEqualTo(Settings.ACTION_NOTIFICATION_HISTORY)
+            assertThat(onClick?.backStack?.map { it.action })
+                .containsExactly(Settings.ACTION_NOTIFICATION_SETTINGS)
+        }
+
     @Test
     fun manageButtonVisible_whenMessageVisible() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 1f4e80e..740abf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
-import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
@@ -85,7 +84,6 @@
             lastWakeReason = WakeSleepReason.OTHER,
             lastSleepReason = WakeSleepReason.OTHER,
         )
-        mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
     }
 
     @Test
@@ -97,14 +95,10 @@
                 lastSleepReason = WakeSleepReason.OTHER,
             )
             keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                )
+                TransitionStep(transitionState = TransitionState.STARTED)
             )
             keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(
-                    to = DozeStateModel.DOZE_AOD,
-                )
+                DozeTransitionModel(to = DozeStateModel.DOZE_AOD)
             )
             val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
@@ -120,14 +114,10 @@
                 lastSleepReason = WakeSleepReason.OTHER,
             )
             keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                )
+                TransitionStep(transitionState = TransitionState.STARTED)
             )
             keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(
-                    to = DozeStateModel.DOZE_PULSING,
-                )
+                DozeTransitionModel(to = DozeStateModel.DOZE_PULSING)
             )
             val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
@@ -186,9 +176,7 @@
                 lastSleepReason = WakeSleepReason.OTHER,
             )
             keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                )
+                TransitionStep(transitionState = TransitionState.STARTED)
             )
             val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
@@ -200,9 +188,7 @@
     fun animationsEnabled_isTrue_whenKeyguardIsShowing() =
         testScope.runTest {
             keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                )
+                TransitionStep(transitionState = TransitionState.STARTED)
             )
             val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a06f4d2..bb9f12b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -19,7 +19,6 @@
 import static android.view.View.GONE;
 import static android.view.WindowInsets.Type.ime;
 
-import static com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
@@ -83,13 +82,13 @@
 import com.android.systemui.shade.QSHeaderBoundsProvider;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
-import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -174,12 +173,6 @@
         mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false);
         mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
 
-        // Register the feature flags we use
-        // TODO: Ideally we wouldn't need to set these unless a test actually reads them,
-        //  and then we would test both configurations, but currently they are all read
-        //  in the constructor.
-        mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION);
-
         // Inject dependencies before initializing the layout
         mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
         mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 3e8bf47..1ef4007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -18,11 +18,11 @@
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.res.R
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
-import com.android.systemui.statusbar.EmptyShadeView
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.RoundableState
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -79,7 +79,7 @@
             /* bypassController */ { false },
             mStatusBarKeyguardViewManager,
             largeScreenShadeInterpolator,
-            avalancheController
+            avalancheController,
         )
 
     private val testableResources = mContext.getOrCreateTestableResources()
@@ -240,7 +240,7 @@
             headsUpTop = headsUpTop,
             stackTop = stackTop,
             collapsedHeight = collapsedHeight,
-            intrinsicHeight = intrinsicHeight
+            intrinsicHeight = intrinsicHeight,
         )
 
         // When
@@ -269,7 +269,7 @@
             headsUpTop = headsUpTop,
             stackTop = stackTop,
             collapsedHeight = collapsedHeight,
-            intrinsicHeight = intrinsicHeight
+            intrinsicHeight = intrinsicHeight,
         )
 
         // When
@@ -548,7 +548,7 @@
         whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
         resetViewStates_expansionChanging_notificationAlphaUpdated(
             expansionFraction = 0.25f,
-            expectedAlpha = 0.0f
+            expectedAlpha = 0.0f,
         )
     }
 
@@ -558,7 +558,7 @@
         whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
         resetViewStates_expansionChanging_notificationAlphaUpdated(
             expansionFraction = 0.85f,
-            expectedAlpha = 0.0f
+            expectedAlpha = 0.0f,
         )
     }
 
@@ -568,7 +568,7 @@
         whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
         resetViewStates_expansionChanging_notificationAlphaUpdated(
             expansionFraction = 0.6f,
-            expectedAlpha = getContentAlpha(0.6f)
+            expectedAlpha = getContentAlpha(0.6f),
         )
     }
 
@@ -785,7 +785,7 @@
         val gap =
             stackScrollAlgorithm.getGapForLocation(
                 /* fractionToShade= */ 0f,
-                /* onKeyguard= */ true
+                /* onKeyguard= */ true,
             )
         assertThat(gap).isEqualTo(smallGap)
     }
@@ -795,7 +795,7 @@
         val gap =
             stackScrollAlgorithm.getGapForLocation(
                 /* fractionToShade= */ 0.5f,
-                /* onKeyguard= */ true
+                /* onKeyguard= */ true,
             )
         assertThat(gap).isEqualTo(smallGap * 0.5f + bigGap * 0.5f)
     }
@@ -805,7 +805,7 @@
         val gap =
             stackScrollAlgorithm.getGapForLocation(
                 /* fractionToShade= */ 0f,
-                /* onKeyguard= */ false
+                /* onKeyguard= */ false,
             )
         assertThat(gap).isEqualTo(bigGap)
     }
@@ -869,7 +869,7 @@
             /* mustStayOnScreen= */ true,
             /* isViewEndVisible= */ true,
             /* viewEnd= */ 0f,
-            /* maxHunY= */ 10f
+            /* maxHunY= */ 10f,
         )
 
         assertTrue(expandableViewState.headsUpIsVisible)
@@ -886,7 +886,7 @@
             /* mustStayOnScreen= */ true,
             /* isViewEndVisible= */ true,
             /* viewEnd= */ 10f,
-            /* maxHunY= */ 0f
+            /* maxHunY= */ 0f,
         )
 
         assertFalse(expandableViewState.headsUpIsVisible)
@@ -903,7 +903,7 @@
             /* mustStayOnScreen= */ true,
             /* isViewEndVisible= */ true,
             /* viewEnd= */ 10f,
-            /* maxHunY= */ 1f
+            /* maxHunY= */ 1f,
         )
 
         assertTrue(expandableViewState.headsUpIsVisible)
@@ -920,7 +920,7 @@
             /* mustStayOnScreen= */ false,
             /* isViewEndVisible= */ true,
             /* viewEnd= */ 10f,
-            /* maxHunY= */ 1f
+            /* maxHunY= */ 1f,
         )
 
         assertTrue(expandableViewState.headsUpIsVisible)
@@ -937,7 +937,7 @@
             /* mustStayOnScreen= */ true,
             /* isViewEndVisible= */ false,
             /* viewEnd= */ 10f,
-            /* maxHunY= */ 1f
+            /* maxHunY= */ 1f,
         )
 
         assertTrue(expandableViewState.headsUpIsVisible)
@@ -951,7 +951,7 @@
         stackScrollAlgorithm.clampHunToTop(
             /* headsUpTop= */ 10f,
             /* collapsedHeight= */ 1f,
-            expandableViewState
+            expandableViewState,
         )
 
         // qqs (10 + 0) < viewY (50)
@@ -966,7 +966,7 @@
         stackScrollAlgorithm.clampHunToTop(
             /* headsUpTop= */ 10f,
             /* collapsedHeight= */ 1f,
-            expandableViewState
+            expandableViewState,
         )
 
         // qqs (10 + 0) > viewY (-10)
@@ -982,7 +982,7 @@
         stackScrollAlgorithm.clampHunToTop(
             /* headsUpTop= */ 10f,
             /* collapsedHeight= */ 10f,
-            expandableViewState
+            expandableViewState,
         )
 
         // newTranslation = max(10, -100) = 10
@@ -1000,7 +1000,7 @@
         stackScrollAlgorithm.clampHunToTop(
             /* headsUpTop= */ 10f,
             /* collapsedHeight= */ 10f,
-            expandableViewState
+            expandableViewState,
         )
 
         // newTranslation = max(10, 5) = 10
@@ -1016,7 +1016,7 @@
                 /* hostViewHeight= */ 100f,
                 /* stackY= */ 110f,
                 /* viewMaxHeight= */ 20f,
-                /* originalCornerRoundness= */ 0f
+                /* originalCornerRoundness= */ 0f,
             )
         assertEquals(1f, currentRoundness)
     }
@@ -1028,7 +1028,7 @@
                 /* hostViewHeight= */ 100f,
                 /* stackY= */ 90f,
                 /* viewMaxHeight= */ 20f,
-                /* originalCornerRoundness= */ 0f
+                /* originalCornerRoundness= */ 0f,
             )
         assertEquals(0.5f, currentRoundness)
     }
@@ -1040,7 +1040,7 @@
                 /* hostViewHeight= */ 100f,
                 /* stackY= */ 0f,
                 /* viewMaxHeight= */ 20f,
-                /* originalCornerRoundness= */ 0f
+                /* originalCornerRoundness= */ 0f,
             )
         assertEquals(0f, currentRoundness)
     }
@@ -1052,7 +1052,7 @@
                 /* hostViewHeight= */ 100f,
                 /* stackY= */ 0f,
                 /* viewMaxHeight= */ 20f,
-                /* originalCornerRoundness= */ 1f
+                /* originalCornerRoundness= */ 1f,
             )
         assertEquals(1f, currentRoundness)
     }
@@ -1076,7 +1076,7 @@
             /* childrenOnTop= */ 0.0f,
             /* StackScrollAlgorithmState= */ algorithmState,
             /* ambientState= */ ambientState,
-            /* shouldElevateHun= */ true
+            /* shouldElevateHun= */ true,
         )
 
         // Then: full shadow would be applied
@@ -1104,7 +1104,7 @@
             /* childrenOnTop= */ 0.0f,
             /* StackScrollAlgorithmState= */ algorithmState,
             /* ambientState= */ ambientState,
-            /* shouldElevateHun= */ true
+            /* shouldElevateHun= */ true,
         )
 
         // Then: HUN should have shadow, but not as full size
@@ -1137,7 +1137,7 @@
             /* childrenOnTop= */ 0.0f,
             /* StackScrollAlgorithmState= */ algorithmState,
             /* ambientState= */ ambientState,
-            /* shouldElevateHun= */ true
+            /* shouldElevateHun= */ true,
         )
 
         // Then: HUN should not have shadow
@@ -1166,7 +1166,7 @@
             /* childrenOnTop= */ 0.0f,
             /* StackScrollAlgorithmState= */ algorithmState,
             /* ambientState= */ ambientState,
-            /* shouldElevateHun= */ true
+            /* shouldElevateHun= */ true,
         )
 
         // Then: HUN should have full shadow
@@ -1196,7 +1196,7 @@
             /* childrenOnTop= */ 0.0f,
             /* StackScrollAlgorithmState= */ algorithmState,
             /* ambientState= */ ambientState,
-            /* shouldElevateHun= */ true
+            /* shouldElevateHun= */ true,
         )
 
         // Then: HUN should have shadow, but not as full size
@@ -1274,14 +1274,14 @@
         setExpansionFractionWithoutShelfDuringAodToLockScreen(
             ambientState,
             algorithmState,
-            fraction = 0.5f
+            fraction = 0.5f,
         )
         stackScrollAlgorithm.resetViewStates(ambientState, 0)
 
         // Then: pulsingNotificationView should show at full height
         assertEquals(
             stackScrollAlgorithm.getMaxAllowedChildHeight(pulsingNotificationView),
-            pulsingNotificationView.viewState.height
+            pulsingNotificationView.viewState.height,
         )
 
         // After: reset dozeAmount and expansionFraction
@@ -1289,7 +1289,7 @@
         setExpansionFractionWithoutShelfDuringAodToLockScreen(
             ambientState,
             algorithmState,
-            fraction = 1f
+            fraction = 1f,
         )
     }
 
@@ -1302,7 +1302,7 @@
                     /* headsUpIsVisible= */ false,
                     /* showingPulsing= */ false,
                     /* isOnKeyguard=*/ false,
-                    /*headsUpOnKeyguard=*/ false
+                    /*headsUpOnKeyguard=*/ false,
                 )
             )
             .isFalse()
@@ -1316,7 +1316,7 @@
                     /* headsUpIsVisible= */ true,
                     /* showingPulsing= */ false,
                     /* isOnKeyguard=*/ false,
-                    /*headsUpOnKeyguard=*/ false
+                    /*headsUpOnKeyguard=*/ false,
                 )
             )
             .isFalse()
@@ -1330,7 +1330,7 @@
                     /* headsUpIsVisible= */ false,
                     /* showingPulsing= */ true,
                     /* isOnKeyguard=*/ false,
-                    /* headsUpOnKeyguard= */ false
+                    /* headsUpOnKeyguard= */ false,
                 )
             )
             .isFalse()
@@ -1344,7 +1344,7 @@
                     /* headsUpIsVisible= */ false,
                     /* showingPulsing= */ false,
                     /* isOnKeyguard=*/ true,
-                    /* headsUpOnKeyguard= */ false
+                    /* headsUpOnKeyguard= */ false,
                 )
             )
             .isFalse()
@@ -1358,7 +1358,7 @@
                     /* headsUpIsVisible= */ false,
                     /* showingPulsing= */ false,
                     /* isOnKeyguard=*/ false,
-                    /* headsUpOnKeyguard= */ false
+                    /* headsUpOnKeyguard= */ false,
                 )
             )
             .isTrue()
@@ -1372,7 +1372,7 @@
                     /* headsUpIsVisible= */ false,
                     /* showingPulsing= */ false,
                     /* isOnKeyguard=*/ true,
-                    /* headsUpOnKeyguard= */ true
+                    /* headsUpOnKeyguard= */ true,
                 )
             )
             .isTrue()
@@ -1408,7 +1408,7 @@
     private fun createHunViewMock(
         isShadeOpen: Boolean,
         fullyVisible: Boolean,
-        headerVisibleAmount: Float
+        headerVisibleAmount: Float,
     ) =
         mock<ExpandableNotificationRow>().apply {
             val childViewStateMock = createHunChildViewState(isShadeOpen, fullyVisible)
@@ -1440,7 +1440,7 @@
     private fun setExpansionFractionWithoutShelfDuringAodToLockScreen(
         ambientState: AmbientState,
         algorithmState: StackScrollAlgorithm.StackScrollAlgorithmState,
-        fraction: Float
+        fraction: Float,
     ) {
         // showingShelf: false
         algorithmState.firstViewInShelf = null
@@ -1476,7 +1476,7 @@
 
     private fun resetViewStates_hunsOverlapping_bottomHunClipped(
         topHun: ExpandableNotificationRow,
-        bottomHun: ExpandableNotificationRow
+        bottomHun: ExpandableNotificationRow,
     ) {
         val topHunHeight =
             mContext.resources.getDimensionPixelSize(R.dimen.notification_content_min_height)
@@ -1524,7 +1524,7 @@
         headsUpBottom: Float = headsUpTop + intrinsicHeight, // assume all the space available
         stackTop: Float,
         stackCutoff: Float = 2000f,
-        fullStackHeight: Float = 3000f
+        fullStackHeight: Float = 3000f,
     ) {
         ambientState.headsUpTop = headsUpTop
         ambientState.headsUpBottom = headsUpBottom
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 135fab8..63a560f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS;
 import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
+import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
 
@@ -157,6 +158,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testDisableNone() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -167,6 +169,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -184,6 +187,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
         // GIVEN the status bar hides the system info via disable flags, while there is no event
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -213,6 +217,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
         // GIVEN the status bar hides the system info via disable flags, while there is no event
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -228,8 +233,8 @@
         assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
     }
 
-
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
         // GIVEN the status bar is not disabled
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -245,6 +250,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
         // GIVEN the status bar is not disabled
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
@@ -268,6 +274,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testDisableNotifications() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -285,6 +292,25 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+    public void testDisableNotifications_doesNothingWhenFlagEnabled() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testDisableClock() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -302,7 +328,26 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
+    public void testDisableClock_doesNothingWhenFlagEnabled() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
+
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
+
+        assertEquals(View.VISIBLE, getClockView().getVisibility());
+    }
+
+    @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_shadeOpenAndShouldHide_everythingHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -320,6 +365,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_shadeOpenButNotShouldHide_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -338,6 +384,7 @@
     /** Regression test for b/279790651. */
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -365,6 +412,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_notTransitioningToOccluded_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -380,6 +428,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_isTransitioningToOccluded_everythingHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -395,6 +444,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -425,7 +475,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void disable_noOngoingCall_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -437,7 +487,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -450,7 +500,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -463,7 +513,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void disable_hasOngoingCallButAlsoHun_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -476,7 +526,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void disable_ongoingCallEnded_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -500,7 +550,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
@@ -517,7 +567,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void screenSharingChipsDisabled_ignoresNewCallback() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -551,6 +601,7 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void noOngoingActivity_chipHidden() {
         resumeAndGetFragment();
 
@@ -568,6 +619,7 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() {
         resumeAndGetFragment();
 
@@ -581,8 +633,36 @@
     }
 
     @Test
+    @EnableFlags({
+            FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
+            FLAG_STATUS_BAR_RON_CHIPS,
+            FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+    public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() {
+        resumeAndGetFragment();
+
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasPrimaryOngoingActivity= */ true,
+                /* hasSecondaryOngoingActivity= */ false,
+                /* shouldAnimate= */ false);
+
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasPrimaryOngoingActivity= */ false,
+                /* hasSecondaryOngoingActivity= */ false,
+                /* shouldAnimate= */ false);
+
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
+        assertEquals(View.VISIBLE, getNotificationAreaView().getVisibility());
+    }
+
+    @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() {
         resumeAndGetFragment();
 
@@ -596,6 +676,7 @@
 
     @Test
     @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
         resumeAndGetFragment();
 
@@ -610,7 +691,7 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -627,6 +708,7 @@
 
     @Test
     @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -644,7 +726,7 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -661,6 +743,7 @@
 
     @Test
     @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -678,7 +761,7 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() {
         resumeAndGetFragment();
 
@@ -701,6 +784,7 @@
 
     @Test
     @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() {
         resumeAndGetFragment();
 
@@ -723,6 +807,7 @@
 
     @Test
     @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void secondaryOngoingActivityEnded_chipHidden() {
         resumeAndGetFragment();
 
@@ -745,7 +830,7 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
@@ -764,6 +849,7 @@
 
     @Test
     @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         // Enable animations for testing so that we can verify we still aren't animating
@@ -782,7 +868,7 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
-    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
     public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -815,6 +901,7 @@
 
     @Test
     @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -848,6 +935,7 @@
 
     @Test
     @EnableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void isHomeStatusBarAllowedByScene_false_everythingHidden() {
         resumeAndGetFragment();
 
@@ -861,6 +949,7 @@
 
     @Test
     @EnableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void isHomeStatusBarAllowedByScene_true_everythingShown() {
         resumeAndGetFragment();
 
@@ -874,6 +963,7 @@
 
     @Test
     @EnableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -891,6 +981,7 @@
 
     @Test
     @EnableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -908,6 +999,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() {
         resumeAndGetFragment();
 
@@ -921,6 +1013,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_isDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -932,6 +1025,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_NotDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(false);
@@ -943,6 +1037,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
@@ -953,6 +1048,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
@@ -1006,6 +1102,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testStatusBarIcons_hiddenThroughoutCameraLaunch() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -1028,6 +1125,7 @@
 
     @Test
     @DisableSceneContainer
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -1063,6 +1161,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
     public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() {
         final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
index 997c00c..c435d3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt
@@ -16,10 +16,12 @@
 
 package com.android.systemui.statusbar.phone.fragment
 
+import android.platform.test.annotations.DisableFlags
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
 import junit.framework.Assert.assertEquals
@@ -36,6 +38,7 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
+@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
 class MultiSourceMinAlphaControllerTest : SysuiTestCase() {
 
     private val view = View(context)
@@ -60,7 +63,7 @@
         multiSourceMinAlphaController.animateToAlpha(
             alpha = 0.5f,
             sourceId = TEST_SOURCE_1,
-            duration = TEST_ANIMATION_DURATION
+            duration = TEST_ANIMATION_DURATION,
         )
         animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION)
         assertEquals(0.5f, view.alpha)
@@ -71,7 +74,7 @@
         multiSourceMinAlphaController.animateToAlpha(
             alpha = 0.5f,
             sourceId = TEST_SOURCE_1,
-            duration = TEST_ANIMATION_DURATION
+            duration = TEST_ANIMATION_DURATION,
         )
         multiSourceMinAlphaController.setAlpha(alpha = 0.7f, sourceId = TEST_SOURCE_2)
         multiSourceMinAlphaController.reset()
@@ -94,7 +97,7 @@
         multiSourceMinAlphaController.animateToAlpha(
             alpha = 0f,
             sourceId = TEST_SOURCE_1,
-            duration = TEST_ANIMATION_DURATION
+            duration = TEST_ANIMATION_DURATION,
         )
         animatorTestRule.advanceTimeBy(TEST_ANIMATION_DURATION / 2)
         multiSourceMinAlphaController.setAlpha(alpha = 1f, sourceId = TEST_SOURCE_1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 4fd830d..a8bcfbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -756,6 +756,27 @@
             assertThat(latest!!.level).isEqualTo(4)
         }
 
+    @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    @Test
+    fun satBasedIcon_reportsLevelZeroWhenOutOfService() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.signalLevelIcon)
+
+            // GIVEN a satellite connection
+            connectionRepository.isNonTerrestrial.value = true
+            // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH
+            connectionRepository.inflateSignalStrength.value = true
+
+            connectionRepository.primaryLevel.value = 4
+            assertThat(latest!!.level).isEqualTo(4)
+
+            connectionRepository.isInService.value = false
+            connectionRepository.primaryLevel.value = 4
+
+            // THEN level reports 0, by policy
+            assertThat(latest!!.level).isEqualTo(0)
+        }
+
     private fun createInteractor(
         overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
     ) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
new file mode 100644
index 0000000..5036e77
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.pipeline.shared.domain.interactor
+
+import android.app.StatusBarManager.DISABLE2_NONE
+import android.app.StatusBarManager.DISABLE_CLOCK
+import android.app.StatusBarManager.DISABLE_NONE
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
+import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
+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.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+@SmallTest
+class CollapsedStatusBarInteractorTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+    val testScope = kosmos.testScope
+    val disableFlagsRepo = kosmos.fakeDisableFlagsRepository
+
+    val underTest = kosmos.collapsedStatusBarInteractor
+
+    @Test
+    fun visibilityViaDisableFlags_allDisabled() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.visibilityViaDisableFlags)
+
+            disableFlagsRepo.disableFlags.value =
+                DisableFlagsModel(
+                    DISABLE_CLOCK or DISABLE_NOTIFICATION_ICONS or DISABLE_SYSTEM_INFO,
+                    DISABLE2_NONE,
+                    animate = false,
+                )
+
+            assertThat(latest!!.isClockAllowed).isFalse()
+            assertThat(latest!!.areNotificationIconsAllowed).isFalse()
+            assertThat(latest!!.isSystemInfoAllowed).isFalse()
+        }
+
+    @Test
+    fun visibilityViaDisableFlags_allEnabled() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.visibilityViaDisableFlags)
+
+            disableFlagsRepo.disableFlags.value =
+                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = false)
+
+            assertThat(latest!!.isClockAllowed).isTrue()
+            assertThat(latest!!.areNotificationIconsAllowed).isTrue()
+            assertThat(latest!!.isSystemInfoAllowed).isTrue()
+        }
+
+    @Test
+    fun visibilityViaDisableFlags_animateFalse() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.visibilityViaDisableFlags)
+
+            disableFlagsRepo.disableFlags.value =
+                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = false)
+
+            assertThat(latest!!.animate).isFalse()
+        }
+
+    @Test
+    fun visibilityViaDisableFlags_animateTrue() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.visibilityViaDisableFlags)
+
+            disableFlagsRepo.disableFlags.value =
+                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = true)
+
+            assertThat(latest!!.animate).isTrue()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 7ae6ea5..bd85780 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -16,21 +16,27 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import android.app.StatusBarManager.DISABLE2_NONE
+import android.app.StatusBarManager.DISABLE_CLOCK
+import android.app.StatusBarManager.DISABLE_NONE
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
+import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.view.View
 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.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 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.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -38,27 +44,25 @@
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
 import com.android.systemui.scene.data.repository.sceneContainerRepository
-import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
-import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+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.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
-import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
 import com.android.systemui.statusbar.data.model.StatusBarMode
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
 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.activeNotificationListRepository
-import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
-import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.emptyFlow
@@ -83,21 +87,15 @@
     private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
     private val activeNotificationListRepository = kosmos.activeNotificationListRepository
     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository
 
-    private val underTest =
-        CollapsedStatusBarViewModelImpl(
-            kosmos.lightsOutInteractor,
-            kosmos.activeNotificationsInteractor,
-            kosmos.keyguardTransitionInteractor,
-            kosmos.sceneInteractor,
-            kosmos.sceneContainerOcclusionInteractor,
-            kosmos.ongoingActivityChipsViewModel,
-            kosmos.applicationCoroutineScope,
-        )
+    private lateinit var underTest: CollapsedStatusBarViewModel
 
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
+        // Initialize here because some flags are checked when this class is constructed
+        underTest = kosmos.collapsedStatusBarViewModel
     }
 
     @Test
@@ -495,14 +493,272 @@
             assertThat(latest).isTrue()
         }
 
+    @Test
+    fun isClockVisible_allowedByDisableFlags_visible() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isClockVisible)
+            transitionKeyguardToGone()
+
+            disableFlagsRepository.disableFlags.value =
+                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+            assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    fun isClockVisible_notAllowedByDisableFlags_gone() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isClockVisible)
+            transitionKeyguardToGone()
+
+            disableFlagsRepository.disableFlags.value =
+                DisableFlagsModel(DISABLE_CLOCK, DISABLE2_NONE)
+
+            assertThat(latest!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    fun isNotificationIconContainerVisible_allowedByDisableFlags_visible() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
+            transitionKeyguardToGone()
+
+            disableFlagsRepository.disableFlags.value =
+                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+            assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    fun isNotificationIconContainerVisible_notAllowedByDisableFlags_gone() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
+            transitionKeyguardToGone()
+
+            disableFlagsRepository.disableFlags.value =
+                DisableFlagsModel(DISABLE_NOTIFICATION_ICONS, DISABLE2_NONE)
+
+            assertThat(latest!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    fun isSystemInfoVisible_allowedByDisableFlags_visible() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isSystemInfoVisible)
+            transitionKeyguardToGone()
+
+            disableFlagsRepository.disableFlags.value =
+                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)
+
+            assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    fun isSystemInfoVisible_notAllowedByDisableFlags_gone() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isSystemInfoVisible)
+            transitionKeyguardToGone()
+
+            disableFlagsRepository.disableFlags.value =
+                DisableFlagsModel(DISABLE_SYSTEM_INFO, DISABLE2_NONE)
+
+            assertThat(latest!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun lockscreenVisible_sceneFlagOff_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = this,
+            )
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun lockscreenVisible_sceneFlagOn_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun bouncerVisible_sceneFlagOff_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.PRIMARY_BOUNCER,
+                testScope = this,
+            )
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun bouncerVisible_sceneFlagOn_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun keyguardIsOccluded_sceneFlagOff_statusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.OCCLUDED,
+                testScope = this,
+            )
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun keyguardIsOccluded_sceneFlagOn_statusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
+            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun keyguardNotShown_sceneFlagOff_statusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            transitionKeyguardToGone()
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun shadeNotShown_sceneFlagOff_statusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            transitionKeyguardToGone()
+
+            kosmos.shadeTestUtil.setShadeExpansion(0f)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun keyguardNotShownAndShadeNotShown_sceneFlagOn_statusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Gone)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE)
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun shadeShown_sceneFlagOff_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            transitionKeyguardToGone()
+
+            kosmos.shadeTestUtil.setShadeExpansion(1f)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun shadeShown_sceneFlagOn_noStatusBarViewsShown() =
+        testScope.runTest {
+            val clockVisible by collectLastValue(underTest.isClockVisible)
+            val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible)
+            val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible)
+            transitionKeyguardToGone()
+
+            kosmos.sceneContainerRepository.snapToScene(Scenes.Shade)
+
+            assertThat(clockVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE)
+            assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE)
+        }
+
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
         ActiveNotificationsStore.Builder()
             .apply { notifications.forEach(::addIndividualNotif) }
             .build()
 
     private val testNotifications =
-        listOf(
-            activeNotificationModel(key = "notif1"),
-            activeNotificationModel(key = "notif2"),
+        listOf(activeNotificationModel(key = "notif1"), activeNotificationModel(key = "notif2"))
+
+    private suspend fun transitionKeyguardToGone() {
+        keyguardTransitionRepository.sendTransitionSteps(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.GONE,
+            testScope = testScope,
         )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
index 4834d36..cc90c11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import android.view.View
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import kotlinx.coroutines.flow.Flow
@@ -36,9 +37,29 @@
 
     override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
 
-    override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
+    override val isClockVisible =
+        MutableStateFlow(
+            CollapsedStatusBarViewModel.VisibilityModel(
+                visibility = View.GONE,
+                shouldAnimateChange = false,
+            )
+        )
 
-    fun setNotificationLightsOut(lightsOut: Boolean) {
-        areNotificationLightsOut.value = lightsOut
-    }
+    override val isNotificationIconContainerVisible =
+        MutableStateFlow(
+            CollapsedStatusBarViewModel.VisibilityModel(
+                visibility = View.GONE,
+                shouldAnimateChange = false,
+            )
+        )
+
+    override val isSystemInfoVisible =
+        MutableStateFlow(
+            CollapsedStatusBarViewModel.VisibilityModel(
+                visibility = View.GONE,
+                shouldAnimateChange = false,
+            )
+        )
+
+    override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
new file mode 100644
index 0000000..0c27e58
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.window.data.repository
+
+import android.app.StatusBarManager.WINDOW_NAVIGATION_BAR
+import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
+import android.app.StatusBarManager.WINDOW_STATE_HIDING
+import android.app.StatusBarManager.WINDOW_STATE_SHOWING
+import android.app.StatusBarManager.WINDOW_STATUS_BAR
+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.statusbar.CommandQueue
+import com.android.systemui.statusbar.commandQueue
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+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.Test
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.argumentCaptor
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class StatusBarWindowStatePerDisplayRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val commandQueue = kosmos.commandQueue
+    private val underTest =
+        StatusBarWindowStatePerDisplayRepositoryImpl(
+            DISPLAY_ID,
+            commandQueue,
+            testScope.backgroundScope,
+        )
+
+    private val callback: CommandQueue.Callbacks
+        get() {
+            testScope.runCurrent()
+            val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+            verify(commandQueue).addCallback(callbackCaptor.capture())
+            return callbackCaptor.firstValue
+        }
+
+    @Test
+    fun windowState_notSameDisplayId_notUpdated() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.windowState)
+            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
+
+            callback.setWindowState(DISPLAY_ID + 1, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+
+            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
+        }
+
+    @Test
+    fun windowState_notStatusBarWindow_notUpdated() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.windowState)
+            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
+
+            callback.setWindowState(DISPLAY_ID, WINDOW_NAVIGATION_BAR, WINDOW_STATE_SHOWING)
+
+            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
+        }
+
+    @Test
+    fun windowState_showing_updated() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.windowState)
+
+            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+
+            assertThat(latest).isEqualTo(StatusBarWindowState.Showing)
+        }
+
+    @Test
+    fun windowState_hiding_updated() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.windowState)
+
+            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDING)
+
+            assertThat(latest).isEqualTo(StatusBarWindowState.Hiding)
+        }
+
+    @Test
+    fun windowState_hidden_updated() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.windowState)
+            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+            assertThat(latest).isEqualTo(StatusBarWindowState.Showing)
+
+            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN)
+
+            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
+        }
+}
+
+private const val DISPLAY_ID = 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
new file mode 100644
index 0000000..b6a3f36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.window.data.repository
+
+import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
+import android.app.StatusBarManager.WINDOW_STATE_SHOWING
+import android.app.StatusBarManager.WINDOW_STATUS_BAR
+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.settings.displayTracker
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.commandQueue
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.reset
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class StatusBarWindowStateRepositoryStoreTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val commandQueue = kosmos.commandQueue
+    private val defaultDisplayId = kosmos.displayTracker.defaultDisplayId
+
+    private val underTest = kosmos.statusBarWindowStateRepositoryStore
+
+    @Test
+    fun defaultDisplay_repoIsForDefaultDisplay() =
+        testScope.runTest {
+            val repo = underTest.defaultDisplay
+            val latest by collectLastValue(repo.windowState)
+
+            testScope.runCurrent()
+            val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+            verify(commandQueue).addCallback(callbackCaptor.capture())
+            val callback = callbackCaptor.firstValue
+
+            // WHEN a default display callback is sent
+            callback.setWindowState(defaultDisplayId, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+
+            // THEN its value is used
+            assertThat(latest).isEqualTo(StatusBarWindowState.Showing)
+
+            // WHEN a non-default display callback is sent
+            callback.setWindowState(defaultDisplayId + 1, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN)
+
+            // THEN its value is NOT used
+            assertThat(latest).isEqualTo(StatusBarWindowState.Showing)
+        }
+
+    @Test
+    fun forDisplay_repoIsForSpecifiedDisplay() =
+        testScope.runTest {
+            // The repository store will always create a repository for the default display, which
+            // will always add a callback to commandQueue. Ignore that callback here.
+            testScope.runCurrent()
+            reset(commandQueue)
+
+            val displayId = defaultDisplayId + 15
+            val repo = underTest.forDisplay(displayId)
+            val latest by collectLastValue(repo.windowState)
+
+            testScope.runCurrent()
+            val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+            verify(commandQueue).addCallback(callbackCaptor.capture())
+            val callback = callbackCaptor.firstValue
+
+            // WHEN a default display callback is sent
+            callback.setWindowState(defaultDisplayId, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+
+            // THEN its value is NOT used
+            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
+
+            // WHEN a callback for this display is sent
+            callback.setWindowState(displayId, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
+
+            // THEN its value is used
+            assertThat(latest).isEqualTo(StatusBarWindowState.Showing)
+        }
+
+    @Test
+    fun forDisplay_reusesRepoForSameDisplayId() =
+        testScope.runTest {
+            // The repository store will always create a repository for the default display, which
+            // will always add a callback to commandQueue. Ignore that callback here.
+            testScope.runCurrent()
+            reset(commandQueue)
+
+            val displayId = defaultDisplayId + 15
+            val firstRepo = underTest.forDisplay(displayId)
+            testScope.runCurrent()
+            val secondRepo = underTest.forDisplay(displayId)
+            testScope.runCurrent()
+
+            assertThat(firstRepo).isEqualTo(secondRepo)
+            // Verify that we only added 1 CommandQueue.Callback because we only created 1 repo.
+            val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+            verify(commandQueue).addCallback(callbackCaptor.capture())
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index c235954..1af0f79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -248,10 +248,17 @@
 
             setUpUsers(count = 2, selectedIndex = 1)
 
+            // WHEN the user switch is starting
+            tracker.onBeforeUserSwitching(userId = 1)
+
+            // THEN the selection status is IN_PROGRESS
+            assertThat(selectedUser!!.selectionStatus)
+                .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
             // WHEN the user is changing
             tracker.onUserChanging(userId = 1)
 
-            // THEN the selection status is IN_PROGRESS
+            // THEN the selection status is still IN_PROGRESS
             assertThat(selectedUser!!.selectionStatus)
                 .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
 
@@ -266,6 +273,7 @@
 
             setUpUsers(count = 2, selectedIndex = 0)
 
+            tracker.onBeforeUserSwitching(userId = 0)
             tracker.onUserChanging(userId = 0)
             assertThat(selectedUser!!.selectionStatus)
                 .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
diff --git a/packages/SystemUI/tests/utils/src/android/view/WindowManagerServiceKosmos.kt b/packages/SystemUI/tests/utils/src/android/view/WindowManagerServiceKosmos.kt
new file mode 100644
index 0000000..cd681a1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/view/WindowManagerServiceKosmos.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 android.view
+
+import android.graphics.Region
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+val Kosmos.mockWindowManagerService: IWindowManager by
+    Kosmos.Fixture {
+        mock(IWindowManager::class.java).apply {
+            whenever(registerSystemGestureExclusionListener(any(), anyInt())).then { answer ->
+                val listener = answer.arguments[0] as ISystemGestureExclusionListener
+                val displayId = answer.arguments[1] as Int
+                exclusionListeners.getOrPut(displayId) { mutableListOf() }.add(listener)
+                listener.onSystemGestureExclusionChanged(
+                    displayId,
+                    restrictedRegionByDisplayId[displayId],
+                    null,
+                )
+            }
+
+            whenever(unregisterSystemGestureExclusionListener(any(), anyInt())).then { answer ->
+                val listener = answer.arguments[0] as ISystemGestureExclusionListener
+                val displayId = answer.arguments[1] as Int
+                exclusionListeners[displayId]?.remove(listener)
+            }
+        }
+    }
+
+var Kosmos.windowManagerService: IWindowManager by Kosmos.Fixture { mockWindowManagerService }
+
+private var restrictedRegionByDisplayId = mutableMapOf<Int, Region?>()
+private var exclusionListeners = mutableMapOf<Int, MutableList<ISystemGestureExclusionListener>>()
+
+fun setSystemGestureExclusionRegion(displayId: Int, restrictedRegion: Region?) {
+    restrictedRegionByDisplayId[displayId] = restrictedRegion
+    exclusionListeners[displayId]?.forEach { listener ->
+        listener.onSystemGestureExclusionChanged(displayId, restrictedRegion, null)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
index 59809e3..79d58a1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
@@ -19,19 +19,27 @@
 import android.content.applicationContext
 import android.view.layoutInflater
 import android.view.windowManager
-import com.android.systemui.biometrics.domain.interactor.sideFpsOverlayInteractor
-import com.android.systemui.biometrics.ui.viewmodel.sideFpsOverlayViewModel
+import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
+import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.sideFpsSensorInteractor
+import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
+@OptIn(ExperimentalCoroutinesApi::class)
 val Kosmos.sideFpsOverlayViewBinder by Fixture {
     SideFpsOverlayViewBinder(
-        applicationCoroutineScope,
-        applicationContext,
+        applicationScope = applicationCoroutineScope,
+        applicationContext = applicationContext,
+        { biometricStatusInteractor },
+        { displayStateInteractor },
+        { deviceEntrySideFpsOverlayInteractor },
         { layoutInflater },
-        { sideFpsOverlayInteractor },
-        { sideFpsOverlayViewModel },
+        { sideFpsProgressBarViewModel },
+        { sideFpsSensorInteractor },
         { windowManager }
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
index e10b2dd..de03855 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelKosmos.kt
@@ -27,9 +27,9 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 val Kosmos.sideFpsOverlayViewModel by Fixture {
     SideFpsOverlayViewModel(
-        applicationContext,
-        deviceEntrySideFpsOverlayInteractor,
-        displayStateInteractor,
-        sideFpsSensorInteractor,
+        applicationContext = applicationContext,
+        deviceEntrySideFpsOverlayInteractor = deviceEntrySideFpsOverlayInteractor,
+        displayStateInteractor = displayStateInteractor,
+        sfpsSensorInteractor = sideFpsSensorInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt
index 8124224..3d41362 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt
@@ -16,9 +16,11 @@
 
 package com.android.systemui.communal.domain.interactor
 
+import android.service.dream.dreamManager
 import com.android.systemui.common.usagestats.domain.interactor.usageStatsInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.shared.system.taskStackChangeListeners
@@ -32,6 +34,8 @@
             keyguardTransitionInteractor = keyguardTransitionInteractor,
             taskStackChangeListeners = taskStackChangeListeners,
             usageStatsInteractor = usageStatsInteractor,
+            dreamManager = dreamManager,
+            bgScope = applicationCoroutineScope,
             logBuffer = logcatLogBuffer("WidgetTrampolineInteractor"),
         )
     }
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 fb12897..12d7c49 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
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
 import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
+import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
@@ -41,6 +42,7 @@
             smartspaceViewModel = keyguardSmartspaceViewModel,
             blueprintInteractor = { keyguardBlueprintInteractor },
             rootViewModel = keyguardRootViewModel,
+            aodBurnInViewModel = aodBurnInViewModel,
         )
     }
 
@@ -95,11 +97,7 @@
     Kosmos.Fixture {
         spy(
             KeyguardBlueprintRepository(
-                blueprints =
-                    setOf(
-                        defaultKeyguardBlueprint,
-                        splitShadeBlueprint,
-                    ),
+                blueprints = setOf(defaultKeyguardBlueprint, splitShadeBlueprint),
                 handler = fakeExecutorHandler,
                 assert = mock(),
             )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
index 64ae051..e6c98cd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.service.dream.dreamManager
+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.deviceentry.domain.interactor.deviceEntryInteractor
@@ -39,10 +41,12 @@
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
             glanceableHubTransitions = glanceableHubTransitions,
+            communalInteractor = communalInteractor,
             communalSceneInteractor = communalSceneInteractor,
             communalSettingsInteractor = communalSettingsInteractor,
             powerInteractor = powerInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+            dreamManager = dreamManager,
             deviceEntryInteractor = deviceEntryInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
index 574bbcd..38bc758 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
@@ -34,10 +33,10 @@
             transitionInteractor = keyguardTransitionInteractor,
             dismissInteractor = keyguardDismissInteractor,
             applicationScope = testScope.backgroundScope,
-            sceneInteractor = { sceneInteractor },
             deviceUnlockedInteractor = { deviceUnlockedInteractor },
             powerInteractor = powerInteractor,
             alternateBouncerInteractor = alternateBouncerInteractor,
             shadeInteractor = { shadeInteractor },
+            keyguardInteractor = { keyguardInteractor },
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
index 52416ba..ace1157 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
@@ -41,6 +41,5 @@
             trustRepository = trustRepository,
             alternateBouncerInteractor = alternateBouncerInteractor,
             powerInteractor = powerInteractor,
-            keyguardTransitionInteractor = keyguardTransitionInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
index 6cf668c..c3c2c8c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
@@ -24,10 +24,12 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 var Kosmos.aodBurnInViewModel by Fixture {
     AodBurnInViewModel(
+        applicationScope = applicationCoroutineScope,
         burnInInteractor = burnInInteractor,
         configurationInteractor = configurationInteractor,
         keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelKosmos.kt
similarity index 64%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelKosmos.kt
index 15c7e25..ef10459 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModelKosmos.kt
@@ -14,19 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics.domain.interactor
+package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@OptIn(ExperimentalCoroutinesApi::class)
-val Kosmos.sideFpsOverlayInteractor by Fixture {
-    SideFpsOverlayInteractorImpl(
-        biometricStatusInteractor,
-        displayStateInteractor,
-        deviceEntrySideFpsOverlayInteractor,
-        sideFpsSensorInteractor,
-    )
+@ExperimentalCoroutinesApi
+val Kosmos.dozingToGlanceableHubTransitionViewModel by Fixture {
+    DozingToGlanceableHubTransitionViewModel(animationFlow = keyguardTransitionAnimationFlow)
 }
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 7cf4083..38626a5 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
@@ -26,6 +26,7 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.ui.viewmodel.notificationShadeWindowModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerAlwaysOnDisplayViewModel
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.dozeParameters
 import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -40,6 +41,7 @@
         communalInteractor = communalInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         notificationsKeyguardInteractor = notificationsKeyguardInteractor,
+        aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel,
         notificationShadeWindowModel = notificationShadeWindowModel,
         alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
         alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index 2deeb25..cfc31c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.jank.interactionJankMonitor
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.scene.domain.interactor.sceneBackInteractor
@@ -36,6 +37,7 @@
             uiEventLogger,
             { interactionJankMonitor },
             mock(),
+            { keyguardInteractor },
             { keyguardTransitionInteractor },
             { shadeInteractor },
             { deviceUnlockedInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
index ff8b478..a80a409 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
@@ -17,13 +17,13 @@
 package com.android.systemui.qs.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
 
 val Kosmos.quickSettingsShadeOverlayContentViewModel: QuickSettingsShadeOverlayContentViewModel by
     Kosmos.Fixture {
         QuickSettingsShadeOverlayContentViewModel(
-            sceneInteractor = sceneInteractor,
+            shadeInteractor = shadeInteractor,
             shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
             quickSettingsContainerViewModel = quickSettingsContainerViewModel,
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index 8744638..737aaf2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -6,13 +6,16 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.interactor.systemGestureExclusionInteractor
 import com.android.systemui.scene.shared.logger.sceneLogger
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.FakeOverlay
+import com.android.systemui.scene.ui.viewmodel.SceneContainerGestureFilter
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
 import com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
+import com.android.systemui.settings.displayTracker
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import kotlinx.coroutines.flow.MutableStateFlow
 
@@ -30,10 +33,7 @@
 val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen }
 
 var Kosmos.overlayKeys by Fixture {
-    listOf(
-        Overlays.NotificationsShade,
-        Overlays.QuickSettingsShade,
-    )
+    listOf(Overlays.NotificationsShade, Overlays.QuickSettingsShade)
 }
 
 val Kosmos.fakeOverlaysByKeys by Fixture { overlayKeys.associateWith { FakeOverlay(it) } }
@@ -74,8 +74,21 @@
             powerInteractor = powerInteractor,
             shadeInteractor = shadeInteractor,
             splitEdgeDetector = splitEdgeDetector,
+            gestureFilterFactory = sceneContainerGestureFilterFactory,
+            displayId = displayTracker.defaultDisplayId,
             motionEventHandlerReceiver = {},
-            logger = sceneLogger
+            logger = sceneLogger,
         )
         .apply { setTransitionState(transitionState) }
 }
+
+val Kosmos.sceneContainerGestureFilterFactory by Fixture {
+    object : SceneContainerGestureFilter.Factory {
+        override fun create(displayId: Int): SceneContainerGestureFilter {
+            return SceneContainerGestureFilter(
+                interactor = systemGestureExclusionInteractor,
+                displayId = displayId,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepositoryKosmos.kt
new file mode 100644
index 0000000..15ed1b3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SystemGestureExclusionRepositoryKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.data.repository
+
+import android.view.windowManagerService
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.systemGestureExclusionRepository by Fixture {
+    SystemGestureExclusionRepository(windowManager = windowManagerService)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractorKosmos.kt
new file mode 100644
index 0000000..3e46c3f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SystemGestureExclusionInteractorKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.scene.data.repository.systemGestureExclusionRepository
+
+val Kosmos.systemGestureExclusionInteractor by Fixture {
+    SystemGestureExclusionInteractor(repository = systemGestureExclusionRepository)
+}
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 6d488d2..60141c6 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
@@ -25,6 +25,7 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.shade.data.repository.ShadeRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.TestScope
@@ -133,7 +134,7 @@
 class ShadeTestUtilLegacyImpl(
     val testScope: TestScope,
     val shadeRepository: FakeShadeRepository,
-    val context: SysuiTestableContext
+    val context: SysuiTestableContext,
 ) : ShadeTestUtilDelegate {
     override fun setShadeAndQsExpansion(shadeExpansion: Float, qsExpansion: Float) {
         shadeRepository.setLegacyShadeExpansion(shadeExpansion)
@@ -191,6 +192,7 @@
 }
 
 /** Sets up shade state for tests when the scene container flag is enabled. */
+@OptIn(ExperimentalCoroutinesApi::class)
 class ShadeTestUtilSceneImpl(
     val testScope: TestScope,
     val sceneInteractor: SceneInteractor,
@@ -269,7 +271,7 @@
         from: SceneKey,
         to: SceneKey,
         progress: Float,
-        isInitiatedByUserInput: Boolean = true
+        isInitiatedByUserInput: Boolean = true,
     ) {
         sceneInteractor.changeScene(from, "test")
         val transitionState =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
index 9cdd519..7a15fdf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
@@ -19,7 +19,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
-import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory
 
 val Kosmos.notificationsShadeOverlayContentViewModel:
@@ -27,6 +27,6 @@
     NotificationsShadeOverlayContentViewModel(
         shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
         notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory,
-        sceneInteractor = sceneInteractor,
+        shadeInteractor = shadeInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
index 7eb9f34..f5b856d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.activityStarter
-import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.privacyChipInteractor
 import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -32,7 +31,6 @@
         ShadeHeaderViewModel(
             context = applicationContext,
             activityStarter = activityStarter,
-            sceneInteractor = sceneInteractor,
             shadeInteractor = shadeInteractor,
             mobileIconsInteractor = mobileIconsInteractor,
             mobileIconsViewModel = mobileIconsViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardStateCallbackInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardStateCallbackInteractorKosmos.kt
new file mode 100644
index 0000000..58dd522
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardStateCallbackInteractorKosmos.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.domain.interactor
+
+import com.android.keyguard.trustManager
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
+import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.trustInteractor
+import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.keyguardStateCallbackInteractor by
+    Kosmos.Fixture {
+        KeyguardStateCallbackInteractor(
+            applicationScope = testScope.backgroundScope,
+            backgroundDispatcher = testDispatcher,
+            selectedUserInteractor = selectedUserInteractor,
+            keyguardTransitionInteractor = keyguardTransitionInteractor,
+            trustInteractor = trustInteractor,
+            simBouncerInteractor = simBouncerInteractor,
+            dismissCallbackRegistry = dismissCallbackRegistry,
+            wmLockscreenVisibilityInteractor = windowManagerLockscreenVisibilityInteractor,
+            trustManager = trustManager,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt
index b19e221..3d2bd6c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt
@@ -45,3 +45,17 @@
             UserHandle.USER_CURRENT,
         )
     }
+
+var Kosmos.lockScreenNotificationMinimalismSetting: Boolean
+    get() =
+        fakeSettings.getIntForUser(
+            Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM,
+            UserHandle.USER_CURRENT,
+        ) == 1
+    set(value) {
+        fakeSettings.putIntForUser(
+            Settings.Secure.LOCK_SCREEN_NOTIFICATION_MINIMALISM,
+            if (value) 1 else 0,
+            UserHandle.USER_CURRENT,
+        )
+    }
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
new file mode 100644
index 0000000..8fdb948
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelKosmos.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.emptyshade.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shared.notifications.domain.interactor.notificationSettingsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
+
+val Kosmos.emptyShadeViewModel by
+    Kosmos.Fixture {
+        EmptyShadeViewModel(
+            applicationContext,
+            zenModeInteractor,
+            seenNotificationsInteractor,
+            notificationSettingsInteractor,
+            dumpManager,
+        )
+    }
+
+val Kosmos.emptyShadeViewModelFactory: EmptyShadeViewModel.Factory by
+    Kosmos.Fixture {
+        object : EmptyShadeViewModel.Factory {
+            override fun create() = emptyShadeViewModel
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
index de8b350..c3bc744 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelKosmos.kt
@@ -23,13 +23,12 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.emptyShadeViewModelFactory
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.footerViewModel
 import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.notificationShelfViewModel
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackInteractor
 import com.android.systemui.statusbar.policy.domain.interactor.userSetupInteractor
-import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import java.util.Optional
 
 val Kosmos.notificationListViewModel by Fixture {
@@ -37,15 +36,14 @@
         notificationShelfViewModel,
         hideListViewModel,
         Optional.of(footerViewModel),
+        emptyShadeViewModelFactory,
         Optional.of(notificationListLoggerViewModel),
         activeNotificationsInteractor,
         notificationStackInteractor,
         headsUpNotificationInteractor,
         remoteInputInteractor,
-        seenNotificationsInteractor,
         shadeInteractor,
         userSetupInteractor,
-        zenModeInteractor,
         testDispatcher,
         dumpManager,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index ffd8aab..a9e117a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.aodToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.aodToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.dozingToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.dozingToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel
@@ -66,6 +67,7 @@
         aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
         aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
+        dozingToGlanceableHubTransitionViewModel = dozingToGlanceableHubTransitionViewModel,
         dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
         dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel,
         dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt
new file mode 100644
index 0000000..385a813
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.pipeline.shared.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+
+val Kosmos.collapsedStatusBarInteractor: CollapsedStatusBarInteractor by
+    Kosmos.Fixture { CollapsedStatusBarInteractor(fakeDisableFlagsRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt
new file mode 100644
index 0000000..1c7fd48
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.pipeline.shared.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+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.ui.viewmodel.ongoingActivityChipsViewModel
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor
+import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor
+
+val Kosmos.collapsedStatusBarViewModel: CollapsedStatusBarViewModel by
+    Kosmos.Fixture {
+        CollapsedStatusBarViewModelImpl(
+            collapsedStatusBarInteractor,
+            lightsOutInteractor,
+            activeNotificationsInteractor,
+            keyguardTransitionInteractor,
+            sceneInteractor,
+            sceneContainerOcclusionInteractor,
+            shadeInteractor,
+            ongoingActivityChipsViewModel,
+            applicationCoroutineScope,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
new file mode 100644
index 0000000..e2b7f5f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.window.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.settings.displayTracker
+import com.android.systemui.statusbar.commandQueue
+
+class KosmosStatusBarWindowStatePerDisplayRepositoryFactory(private val kosmos: Kosmos) :
+    StatusBarWindowStatePerDisplayRepositoryFactory {
+    override fun create(displayId: Int): StatusBarWindowStatePerDisplayRepositoryImpl {
+        return StatusBarWindowStatePerDisplayRepositoryImpl(
+            displayId,
+            kosmos.commandQueue,
+            kosmos.applicationCoroutineScope,
+        )
+    }
+}
+
+val Kosmos.statusBarWindowStateRepositoryStore by
+    Kosmos.Fixture {
+        StatusBarWindowStateRepositoryStoreImpl(
+            displayId = displayTracker.defaultDisplayId,
+            factory = KosmosStatusBarWindowStatePerDisplayRepositoryFactory(this),
+        )
+    }
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 93cdde0..75d07bb 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -1037,14 +1037,12 @@
 
         @Override
         public void onCaptureFailed(int captureSequenceId, int reason) {
-            if (Flags.concertMode()) {
-                if (mCaptureCallback != null) {
-                    try {
-                        mCaptureCallback.onCaptureProcessFailed(captureSequenceId, reason);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Failed to notify capture failure due to remote " +
-                                "exception!");
-                    }
+            if (mCaptureCallback != null) {
+                try {
+                    mCaptureCallback.onCaptureProcessFailed(captureSequenceId, reason);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to notify capture failure due to remote " +
+                            "exception!");
                 }
             }
         }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 10e4f38..6b1197a 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -267,25 +267,39 @@
     visibility: ["//frameworks/base"],
 }
 
-cc_library_shared {
-    name: "libravenwood_runtime",
-    host_supported: true,
-
+cc_defaults {
+    name: "ravenwood_jni_defaults",
     cflags: [
         "-Wall",
         "-Werror",
         "-Wno-unused-parameter",
         "-Wthread-safety",
     ],
-
-    srcs: [
-        "runtime-jni/*.cpp",
+    static_libs: [
+        "libnativehelper_jvm",
     ],
+    shared_libs: [
+        "liblog",
+    ],
+}
 
+// We need this as a separate library because we need to overload the
+// sysprop symbols before libbase is loaded into the process
+cc_library_host_shared {
+    name: "libravenwood_sysprop",
+    defaults: ["ravenwood_jni_defaults"],
+    srcs: ["runtime-jni/ravenwood_sysprop.cpp"],
+}
+
+cc_library_host_shared {
+    name: "libravenwood_runtime",
+    defaults: ["ravenwood_jni_defaults"],
+    srcs: [
+        "runtime-jni/ravenwood_runtime.cpp",
+        "runtime-jni/ravenwood_os_constants.cpp",
+    ],
     shared_libs: [
         "libbase",
-        "liblog",
-        "libnativehelper",
         "libutils",
         "libcutils",
     ],
@@ -377,8 +391,10 @@
         "z00-all-updatable-modules-system-stubs",
     ],
     jni_libs: [
-        "libandroid_runtime",
+        // Libraries has to be loaded in the following order
+        "libravenwood_sysprop",
         "libravenwood_runtime",
+        "libandroid_runtime",
     ],
 }
 
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index d4d188d..86246e2 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -34,15 +34,18 @@
     },
     {
       "name": "CarLibHostUnitTest",
-      "host": true
+      "host": true,
+      "keywords": ["automotive_code_coverage"]
     },
     {
       "name": "CarServiceHostUnitTest",
-      "host": true
+      "host": true,
+      "keywords": ["automotive_code_coverage"]
     },
     {
       "name": "CarSystemUIRavenTests",
-      "host": true
+      "host": true,
+      "keywords": ["automotive_code_coverage"]
     },
     {
       "name": "CtsAccountManagerTestCasesRavenwood",
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
index e548611..d29b93c 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
@@ -43,6 +43,7 @@
             android.util.Log.class,
             android.os.Parcel.class,
             android.os.Binder.class,
+            android.os.SystemProperties.class,
             android.content.res.ApkAssets.class,
             android.content.res.AssetManager.class,
             android.content.res.StringBlock.class,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 2417262..6e73b2c 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -16,12 +16,11 @@
 
 package android.platform.test.ravenwood;
 
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
 
-import static org.junit.Assert.fail;
-
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.ResourcesManager;
@@ -32,6 +31,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Log;
@@ -39,6 +39,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.os.RuntimeInit;
+import com.android.ravenwood.RavenwoodRuntimeNative;
 import com.android.ravenwood.common.RavenwoodCommonUtils;
 import com.android.ravenwood.common.RavenwoodRuntimeException;
 import com.android.ravenwood.common.SneakyThrow;
@@ -69,6 +70,8 @@
     }
 
     private static final String MAIN_THREAD_NAME = "RavenwoodMain";
+    private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
+    private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
 
     /**
      * When enabled, attempt to dump all thread stacks just before we hit the
@@ -119,6 +122,7 @@
     }
 
     private static RavenwoodConfig sConfig;
+    private static RavenwoodSystemProperties sProps;
     private static boolean sInitialized = false;
 
     /**
@@ -133,6 +137,14 @@
         // We haven't initialized liblog yet, so directly write to System.out here.
         RavenwoodCommonUtils.log(TAG, "globalInit()");
 
+        // Load libravenwood_sysprop first
+        var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME);
+        System.load(libProp);
+        RavenwoodRuntimeNative.reloadNativeLibrary(libProp);
+
+        // Make sure libravenwood_runtime is loaded.
+        System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
+
         // Do the basic set up for the android sysprops.
         setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
 
@@ -211,23 +223,21 @@
             var file = new File(RAVENWOOD_RESOURCE_APK);
             return config.mState.loadResources(file.exists() ? file : null);
         };
-        // Set up test context's resources.
+
+        // Set up test context's (== instrumentation context's) resources.
         // If the target package name == test package name, then we use the main resources.
-        // Otherwise, we don't simulate loading resources from the test APK yet.
-        // (we need to add `test_resource_apk` to `android_ravenwood_test`)
-        final Supplier<Resources> testResourcesLoader;
+        final Supplier<Resources> instResourcesLoader;
         if (isSelfInstrumenting) {
-            testResourcesLoader = targetResourcesLoader;
+            instResourcesLoader = targetResourcesLoader;
         } else {
-            testResourcesLoader = () -> {
-                fail("Cannot load resources from the test context (yet)."
-                        + " Use target context's resources instead.");
-                return null; // unreachable.
+            instResourcesLoader = () -> {
+                var file = new File(RAVENWOOD_INST_RESOURCE_APK);
+                return config.mState.loadResources(file.exists() ? file : null);
             };
         }
 
-        var testContext = new RavenwoodContext(
-                config.mTestPackageName, main, testResourcesLoader);
+        var instContext = new RavenwoodContext(
+                config.mTestPackageName, main, instResourcesLoader);
         var targetContext = new RavenwoodContext(
                 config.mTargetPackageName, main, targetResourcesLoader);
 
@@ -236,18 +246,18 @@
                 config.mTargetPackageName, main, targetResourcesLoader);
         appContext.setApplicationContext(appContext);
         if (isSelfInstrumenting) {
-            testContext.setApplicationContext(appContext);
+            instContext.setApplicationContext(appContext);
             targetContext.setApplicationContext(appContext);
         } else {
             // When instrumenting into another APK, the test context doesn't have an app context.
             targetContext.setApplicationContext(appContext);
         }
-        config.mTestContext = testContext;
+        config.mInstContext = instContext;
         config.mTargetContext = targetContext;
 
         // Prepare other fields.
         config.mInstrumentation = new Instrumentation();
-        config.mInstrumentation.basicInit(config.mTestContext, config.mTargetContext);
+        config.mInstrumentation.basicInit(config.mInstContext, config.mTargetContext);
         InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY);
 
         RavenwoodSystemServer.init(config);
@@ -284,13 +294,13 @@
 
         InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
         config.mInstrumentation = null;
-        if (config.mTestContext != null) {
-            ((RavenwoodContext) config.mTestContext).cleanUp();
+        if (config.mInstContext != null) {
+            ((RavenwoodContext) config.mInstContext).cleanUp();
         }
         if (config.mTargetContext != null) {
             ((RavenwoodContext) config.mTargetContext).cleanUp();
         }
-        config.mTestContext = null;
+        config.mInstContext = null;
         config.mTargetContext = null;
 
         if (config.mProvideMainThread) {
@@ -358,12 +368,21 @@
     /**
      * Set the current configuration to the actual SystemProperties.
      */
-    public static void setSystemProperties(RavenwoodSystemProperties ravenwoodSystemProperties) {
-        var clone = new RavenwoodSystemProperties(ravenwoodSystemProperties, true);
+    private static void setSystemProperties(RavenwoodSystemProperties systemProperties) {
+        SystemProperties.clearChangeCallbacksForTest();
+        RavenwoodRuntimeNative.clearSystemProperties();
+        sProps = new RavenwoodSystemProperties(systemProperties, true);
+        for (var entry : systemProperties.getValues().entrySet()) {
+            RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
+        }
+    }
 
-        android.os.SystemProperties.init$ravenwood(
-                clone.getValues(),
-                clone.getKeyReadablePredicate(),
-                clone.getKeyWritablePredicate());
+    @SuppressWarnings("unused")  // Called from native code (ravenwood_sysprop.cpp)
+    private static void checkSystemPropertyAccess(String key, boolean write) {
+        boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key);
+        if (!result) {
+            throw new IllegalArgumentException((write ? "Write" : "Read")
+                    + " access to system property '" + key + "' denied via RavenwoodConfig");
+        }
     }
 }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index d4090e2..3946dd84 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -67,7 +67,7 @@
 
         sStartedServices = new ArraySet<>();
         sTimings = new TimingsTraceAndSlog();
-        sServiceManager = new SystemServiceManager(config.mTestContext);
+        sServiceManager = new SystemServiceManager(config.mInstContext);
         sServiceManager.setStartInfo(false,
                 SystemClock.elapsedRealtime(),
                 SystemClock.uptimeMillis());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 5d251bd..5ba972df 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -167,6 +167,7 @@
         return runner;
     }
 
+    private final Class<?> mTestJavaClass;
     private TestClass mTestClass = null;
     private Runner mRealRunner = null;
     private Description mDescription = null;
@@ -192,6 +193,7 @@
      * Constructor.
      */
     public RavenwoodAwareTestRunner(Class<?> testClass) {
+        mTestJavaClass = testClass;
         try {
             performGlobalInitialization();
 
@@ -320,7 +322,7 @@
             return;
         }
 
-        Log.v(TAG, "Starting " + mTestClass.getJavaClass().getCanonicalName());
+        Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName());
         if (RAVENWOOD_VERBOSE_LOGGING) {
             dumpDescription(getDescription());
         }
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index ea33aa6..446f819 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -74,7 +74,7 @@
 
     final List<Class<?>> mServicesRequired = new ArrayList<>();
 
-    volatile Context mTestContext;
+    volatile Context mInstContext;
     volatile Context mTargetContext;
     volatile Instrumentation mInstrumentation;
 
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 984106b..4196d8e 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -216,7 +216,7 @@
      */
     @Deprecated
     public Context getContext() {
-        return Objects.requireNonNull(mConfiguration.mTestContext,
+        return Objects.requireNonNull(mConfiguration.mInstContext,
                 "Context is only available during @Test execution");
     }
 
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index ef8f584..f1e1ef6 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -22,7 +22,6 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Predicate;
 
 public class RavenwoodSystemProperties {
     private volatile boolean mIsImmutable;
@@ -31,47 +30,9 @@
 
     /** Set of additional keys that should be considered readable */
     private final Set<String> mKeyReadable = new HashSet<>();
-    private final Predicate<String> mKeyReadablePredicate = (key) -> {
-        final String root = getKeyRoot(key);
-
-        if (root.startsWith("debug.")) return true;
-
-        // This set is carefully curated to help identify situations where a test may
-        // accidentally depend on a default value of an obscure property whose owner hasn't
-        // decided how Ravenwood should behave.
-        if (root.startsWith("boot.")) return true;
-        if (root.startsWith("build.")) return true;
-        if (root.startsWith("product.")) return true;
-        if (root.startsWith("soc.")) return true;
-        if (root.startsWith("system.")) return true;
-
-        switch (key) {
-            case "gsm.version.baseband":
-            case "no.such.thing":
-            case "qemu.sf.lcd_density":
-            case "ro.bootloader":
-            case "ro.debuggable":
-            case "ro.hardware":
-            case "ro.hw_timeout_multiplier":
-            case "ro.odm.build.media_performance_class":
-            case "ro.sf.lcd_density":
-            case "ro.treble.enabled":
-            case "ro.vndk.version":
-                return true;
-        }
-
-        return mKeyReadable.contains(key);
-    };
 
     /** Set of additional keys that should be considered writable */
     private final Set<String> mKeyWritable = new HashSet<>();
-    private final Predicate<String> mKeyWritablePredicate = (key) -> {
-        final String root = getKeyRoot(key);
-
-        if (root.startsWith("debug.")) return true;
-
-        return mKeyWritable.contains(key);
-    };
 
     public RavenwoodSystemProperties() {
         // TODO: load these values from build.prop generated files
@@ -121,12 +82,45 @@
         return new HashMap<>(mValues);
     }
 
-    public Predicate<String> getKeyReadablePredicate() {
-        return mKeyReadablePredicate;
+    public boolean isKeyReadable(String key) {
+        final String root = getKeyRoot(key);
+
+        if (root.startsWith("debug.")) return true;
+
+        // This set is carefully curated to help identify situations where a test may
+        // accidentally depend on a default value of an obscure property whose owner hasn't
+        // decided how Ravenwood should behave.
+        if (root.startsWith("boot.")) return true;
+        if (root.startsWith("build.")) return true;
+        if (root.startsWith("product.")) return true;
+        if (root.startsWith("soc.")) return true;
+        if (root.startsWith("system.")) return true;
+
+        switch (key) {
+            case "gsm.version.baseband":
+            case "no.such.thing":
+            case "qemu.sf.lcd_density":
+            case "ro.bootloader":
+            case "ro.debuggable":
+            case "ro.hardware":
+            case "ro.hw_timeout_multiplier":
+            case "ro.odm.build.media_performance_class":
+            case "ro.sf.lcd_density":
+            case "ro.treble.enabled":
+            case "ro.vndk.version":
+            case "ro.icu.data.path":
+                return true;
+        }
+
+        return mKeyReadable.contains(key);
     }
 
-    public Predicate<String> getKeyWritablePredicate() {
-        return mKeyWritablePredicate;
+    public boolean isKeyWritable(String key) {
+        final String root = getKeyRoot(key);
+
+        if (root.startsWith("debug.")) return true;
+
+        return mKeyWritable.contains(key);
     }
 
     private static final String[] PARTITIONS = {
@@ -208,4 +202,4 @@
     // Create a default instance, and make an immutable copy of it.
     public static final RavenwoodSystemProperties DEFAULT_VALUES =
             new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true);
-}
\ No newline at end of file
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 96746c6..ef795c6 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -47,9 +47,6 @@
     public static final boolean RAVENWOOD_VERBOSE_LOGGING = "1".equals(System.getenv(
             "RAVENWOOD_VERBOSE"));
 
-    /** Name of `libravenwood_runtime` */
-    private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
-
     /** Directory name of `out/host/linux-x86/testcases/ravenwood-runtime` */
     private static final String RAVENWOOD_RUNTIME_DIR_NAME = "ravenwood-runtime";
 
@@ -63,6 +60,8 @@
     public static final String RAVENWOOD_SYSPROP = "ro.is_on_ravenwood";
 
     public static final String RAVENWOOD_RESOURCE_APK = "ravenwood-res-apks/ravenwood-res.apk";
+    public static final String RAVENWOOD_INST_RESOURCE_APK =
+            "ravenwood-res-apks/ravenwood-inst-res.apk";
 
     public static final String RAVENWOOD_EMPTY_RESOURCES_APK =
             RAVENWOOD_RUNTIME_PATH + "ravenwood-data/ravenwood-empty-res.apk";
@@ -108,29 +107,21 @@
     }
 
     /**
-     * Load the main runtime JNI library.
-     */
-    public static void loadRavenwoodNativeRuntime() {
-        ensureOnRavenwood();
-        loadJniLibrary(RAVENWOOD_NATIVE_RUNTIME_NAME);
-    }
-
-    /**
      * Internal implementation of
      * {@link android.platform.test.ravenwood.RavenwoodUtils#loadJniLibrary(String)}
      */
     public static void loadJniLibrary(String libname) {
         if (RavenwoodCommonUtils.isOnRavenwood()) {
-            loadJniLibraryInternal(libname);
+            System.load(getJniLibraryPath(libname));
         } else {
             System.loadLibrary(libname);
         }
     }
 
     /**
-     * Function equivalent to ART's System.loadLibrary. See RavenwoodUtils for why we need it.
+     * Find the shared library path from java.library.path.
      */
-    private static void loadJniLibraryInternal(String libname) {
+    public static String getJniLibraryPath(String libname) {
         var path = System.getProperty("java.library.path");
         var filename = "lib" + libname + ".so";
 
@@ -138,22 +129,21 @@
 
         try {
             if (path == null) {
-                throw new UnsatisfiedLinkError("Cannot load library " + libname + "."
+                throw new UnsatisfiedLinkError("Cannot find library " + libname + "."
                         + " Property java.library.path not set!");
             }
             for (var dir : path.split(":")) {
                 var file = new File(dir + "/" + filename);
                 if (file.exists()) {
-                    System.load(file.getAbsolutePath());
-                    return;
+                    return file.getAbsolutePath();
                 }
             }
-            throw new UnsatisfiedLinkError("Library " + libname + " not found in "
-                    + "java.library.path: " + path);
         } catch (Throwable e) {
             dumpFiles(System.out);
             throw e;
         }
+        throw new UnsatisfiedLinkError("Library " + libname + " not found in "
+                + "java.library.path: " + path);
     }
 
     private static void dumpFiles(PrintStream out) {
diff --git a/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java b/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java
deleted file mode 100644
index b09bf31..0000000
--- a/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.os;
-
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Predicate;
-
-public class SystemProperties_host {
-    private static final Object sLock = new Object();
-
-    /** Active system property values */
-    @GuardedBy("sLock")
-    private static Map<String, String> sValues;
-    /** Predicate tested to determine if a given key can be read. */
-    @GuardedBy("sLock")
-    private static Predicate<String> sKeyReadablePredicate;
-    /** Predicate tested to determine if a given key can be written. */
-    @GuardedBy("sLock")
-    private static Predicate<String> sKeyWritablePredicate;
-
-    /**
-     * Reverse mapping that provides a way back to an original key from the
-     * {@link System#identityHashCode(Object)} of {@link String#intern}.
-     */
-    @GuardedBy("sLock")
-    private static SparseArray<String> sKeyHandles = new SparseArray<>();
-
-    /**
-     * Basically the same as {@link #init$ravenwood}, but it'll only run if no values are
-     * set yet.
-     */
-    public static void initializeIfNeeded(Map<String, String> values,
-            Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) {
-        synchronized (sLock) {
-            if (sValues != null) {
-                return; // Already initialized.
-            }
-            init$ravenwood(values, keyReadablePredicate, keyWritablePredicate);
-        }
-    }
-
-    public static void init$ravenwood(Map<String, String> values,
-            Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) {
-        synchronized (sLock) {
-            sValues = Objects.requireNonNull(values);
-            sKeyReadablePredicate = Objects.requireNonNull(keyReadablePredicate);
-            sKeyWritablePredicate = Objects.requireNonNull(keyWritablePredicate);
-            sKeyHandles.clear();
-            synchronized (SystemProperties.sChangeCallbacks) {
-                SystemProperties.sChangeCallbacks.clear();
-            }
-        }
-    }
-
-    public static void reset$ravenwood() {
-        synchronized (sLock) {
-            sValues = null;
-            sKeyReadablePredicate = null;
-            sKeyWritablePredicate = null;
-            sKeyHandles.clear();
-            synchronized (SystemProperties.sChangeCallbacks) {
-                SystemProperties.sChangeCallbacks.clear();
-            }
-        }
-    }
-
-    public static void native_set(String key, String val) {
-        synchronized (sLock) {
-            Objects.requireNonNull(key);
-            Preconditions.requireNonNullViaRavenwoodRule(sValues);
-            if (!sKeyWritablePredicate.test(key)) {
-                throw new IllegalArgumentException(
-                        "Write access to system property '" + key + "' denied via RavenwoodRule");
-            }
-            if (key.startsWith("ro.") && sValues.containsKey(key)) {
-                throw new IllegalArgumentException(
-                        "System property '" + key + "' already defined once; cannot redefine");
-            }
-            if ((val == null) || val.isEmpty()) {
-                sValues.remove(key);
-            } else {
-                sValues.put(key, val);
-            }
-            SystemProperties.callChangeCallbacks();
-        }
-    }
-
-    public static String native_get(String key, String def) {
-        synchronized (sLock) {
-            Objects.requireNonNull(key);
-            Preconditions.requireNonNullViaRavenwoodRule(sValues);
-            if (!sKeyReadablePredicate.test(key)) {
-                throw new IllegalArgumentException(
-                        "Read access to system property '" + key + "' denied via RavenwoodRule");
-            }
-            return sValues.getOrDefault(key, def);
-        }
-    }
-
-    public static int native_get_int(String key, int def) {
-        try {
-            return Integer.parseInt(native_get(key, ""));
-        } catch (NumberFormatException ignored) {
-            return def;
-        }
-    }
-
-    public static long native_get_long(String key, long def) {
-        try {
-            return Long.parseLong(native_get(key, ""));
-        } catch (NumberFormatException ignored) {
-            return def;
-        }
-    }
-
-    public static boolean native_get_boolean(String key, boolean def) {
-        return parseBoolean(native_get(key, ""), def);
-    }
-
-    public static long native_find(String name) {
-        synchronized (sLock) {
-            Preconditions.requireNonNullViaRavenwoodRule(sValues);
-            if (sValues.containsKey(name)) {
-                name = name.intern();
-                final int handle = System.identityHashCode(name);
-                sKeyHandles.put(handle, name);
-                return handle;
-            } else {
-                return 0;
-            }
-        }
-    }
-
-    public static String native_get(long handle) {
-        synchronized (sLock) {
-            return native_get(sKeyHandles.get((int) handle), "");
-        }
-    }
-
-    public static int native_get_int(long handle, int def) {
-        synchronized (sLock) {
-            return native_get_int(sKeyHandles.get((int) handle), def);
-        }
-    }
-
-    public static long native_get_long(long handle, long def) {
-        synchronized (sLock) {
-            return native_get_long(sKeyHandles.get((int) handle), def);
-        }
-    }
-
-    public static boolean native_get_boolean(long handle, boolean def) {
-        synchronized (sLock) {
-            return native_get_boolean(sKeyHandles.get((int) handle), def);
-        }
-    }
-
-    public static void native_add_change_callback() {
-        // Ignored; callback always registered via init above
-    }
-
-    public static void native_report_sysprop_change() {
-        // Report through callback always registered via init above
-        synchronized (sLock) {
-            Preconditions.requireNonNullViaRavenwoodRule(sValues);
-            SystemProperties.callChangeCallbacks();
-        }
-    }
-
-    private static boolean parseBoolean(String val, boolean def) {
-        // Matches system/libbase/include/android-base/parsebool.h
-        if (val == null) return def;
-        switch (val) {
-            case "1":
-            case "on":
-            case "true":
-            case "y":
-            case "yes":
-                return true;
-            case "0":
-            case "false":
-            case "n":
-            case "no":
-            case "off":
-                return false;
-            default:
-                return def;
-        }
-    }
-}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java
index c56ec8a..3fedc1a 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java
@@ -15,8 +15,6 @@
  */
 package android.system;
 
-import com.android.ravenwood.common.RavenwoodCommonUtils;
-
 /**
  * Copied from libcore's version, with the local changes:
  * - All the imports are removed. (they're only used in javadoc)
@@ -1252,8 +1250,6 @@
     private static int placeholder() { return 0; }
     // ...because we want to initialize them at runtime.
     static {
-        // [ravenwood-change] Load the JNI lib.
-        RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
         Native.initConstants();
     }
 }
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index ad80d92..f13189f 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -19,7 +19,6 @@
 import android.system.StructStat;
 
 import com.android.ravenwood.common.JvmWorkaround;
-import com.android.ravenwood.common.RavenwoodCommonUtils;
 
 import java.io.FileDescriptor;
 
@@ -30,11 +29,6 @@
     private RavenwoodRuntimeNative() {
     }
 
-    static {
-        RavenwoodCommonUtils.ensureOnRavenwood();
-        RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
-    }
-
     public static native void applyFreeFunction(long freeFunction, long nativePtr);
 
     private static native long nLseek(int fd, long offset, int whence) throws ErrnoException;
@@ -56,6 +50,14 @@
     public static native void setenv(String name, String value, boolean overwrite)
             throws ErrnoException;
 
+    public static native void reloadNativeLibrary(String libFile);
+
+    public static native String getSystemProperty(String key);
+
+    public static native boolean setSystemProperty(String key, String value);
+
+    public static native void clearSystemProperties();
+
     public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
         return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
     }
diff --git a/ravenwood/runtime-jni/jni_helper.h b/ravenwood/runtime-jni/jni_helper.h
new file mode 100644
index 0000000..561fb3b
--- /dev/null
+++ b/ravenwood/runtime-jni/jni_helper.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include <string>
+
+constexpr const char* kCommonUtils = "com/android/ravenwood/common/RavenwoodCommonUtils";
+constexpr const char* kRuntimeEnvController =
+        "android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController";
+constexpr const char* kRuntimeNative = "com/android/ravenwood/RavenwoodRuntimeNative";
+
+// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars
+// we only get modified UTF-8, which is not the platform string type used in host JVM.
+struct ScopedRealUtf8Chars {
+    ScopedRealUtf8Chars(JNIEnv* env, jstring s) : valid_(false) {
+        if (s == nullptr) {
+            jniThrowNullPointerException(env);
+            return;
+        }
+        jclass clazz = env->GetObjectClass(s);
+        jmethodID getBytes = env->GetMethodID(clazz, "getBytes", "(Ljava/lang/String;)[B");
+
+        ScopedLocalRef<jstring> utf8(env, env->NewStringUTF("UTF-8"));
+        ScopedLocalRef<jbyteArray> jbytes(env,
+                                          (jbyteArray)env->CallObjectMethod(s, getBytes,
+                                                                            utf8.get()));
+
+        ScopedByteArrayRO bytes(env, jbytes.get());
+        string_.append((const char*)bytes.get(), bytes.size());
+        valid_ = true;
+    }
+
+    const char* c_str() const {
+        return valid_ ? string_.c_str() : nullptr;
+    }
+
+    size_t size() const {
+        return string_.size();
+    }
+
+    const char& operator[](size_t n) const {
+        return string_[n];
+    }
+
+private:
+    std::string string_;
+    bool valid_;
+};
+
+static inline JNIEnv* GetJNIEnvOrDie(JavaVM* vm) {
+    JNIEnv* env = nullptr;
+    vm->GetEnv((void**)&env, JNI_VERSION_1_4);
+    LOG_ALWAYS_FATAL_IF(env == nullptr, "Could not retrieve JNIEnv.");
+    return env;
+}
+
+static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
+    jclass clazz = env->FindClass(class_name);
+    LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
+    return clazz;
+}
+
+template <typename T>
+static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) {
+    jobject res = env->NewGlobalRef(in);
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference.");
+    return static_cast<T>(res);
+}
+
+static inline jclass FindGlobalClassOrDie(JNIEnv* env, const char* class_name) {
+    return MakeGlobalRefOrDie(env, FindClassOrDie(env, class_name));
+}
+
+static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,
+                                               const char* method_signature) {
+    jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature);
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s",
+                        method_name, method_signature);
+    return res;
+}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index c255be5..3ff0848 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -15,21 +15,14 @@
  */
 
 #include <fcntl.h>
-#include <sys/stat.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <unistd.h>
+#include <utils/misc.h>
+
 #include <string>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
 
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-
-// Defined in ravenwood_os_constants.cpp
-void register_android_system_OsConstants(JNIEnv* env);
+#include "jni_helper.h"
 
 // ---- Exception related ----
 
@@ -51,53 +44,6 @@
 static jclass g_StructStat;
 static jclass g_StructTimespecClass;
 
-// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars
-// we only get modified UTF-8, which is not the platform string type used in host JVM.
-struct ScopedRealUtf8Chars {
-    ScopedRealUtf8Chars(JNIEnv* env, jstring s) : valid_(false) {
-        if (s == nullptr) {
-            jniThrowNullPointerException(env);
-            return;
-        }
-        jclass clazz = env->GetObjectClass(s);
-        jmethodID getBytes = env->GetMethodID(clazz, "getBytes", "(Ljava/lang/String;)[B");
-
-        ScopedLocalRef<jstring> utf8(env, env->NewStringUTF("UTF-8"));
-        ScopedLocalRef<jbyteArray> jbytes(env,
-            (jbyteArray) env->CallObjectMethod(s, getBytes, utf8.get()));
-
-        ScopedByteArrayRO bytes(env, jbytes.get());
-        string_.append((const char *) bytes.get(), bytes.size());
-        valid_ = true;
-    }
-
-    const char* c_str() const {
-        return valid_ ? string_.c_str() : nullptr;
-    }
-
-    size_t size() const {
-        return string_.size();
-    }
-
-    const char& operator[](size_t n) const {
-        return string_[n];
-    }
-
-private:
-    std::string string_;
-    bool valid_;
-};
-
-static jclass findClass(JNIEnv* env, const char* name) {
-    ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
-    jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
-    if (result == NULL) {
-        ALOGE("failed to find class '%s'", name);
-        abort();
-    }
-    return result;
-}
-
 static jobject makeStructTimespec(JNIEnv* env, const struct timespec& ts) {
     static jmethodID ctor = env->GetMethodID(g_StructTimespecClass, "<init>",
             "(JJ)V");
@@ -229,6 +175,8 @@
 
 // ---- Registration ----
 
+extern void register_android_system_OsConstants(JNIEnv* env);
+
 static const JNINativeMethod sMethods[] =
 {
     { "applyFreeFunction", "(JJ)V", (void*)nApplyFreeFunction },
@@ -243,24 +191,14 @@
     { "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv },
 };
 
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
-{
-    JNIEnv* env = NULL;
-    jint result = -1;
-
-    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-        ALOGE("GetEnv failed!");
-        return result;
-    }
-    ALOG_ASSERT(env, "Could not retrieve the env!");
-
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
     ALOGI("%s: JNI_OnLoad", __FILE__);
 
-    g_StructStat = findClass(env, "android/system/StructStat");
-    g_StructTimespecClass = findClass(env, "android/system/StructTimespec");
+    JNIEnv* env = GetJNIEnvOrDie(vm);
+    g_StructStat = FindGlobalClassOrDie(env, "android/system/StructStat");
+    g_StructTimespecClass = FindGlobalClassOrDie(env, "android/system/StructTimespec");
 
-    jint res = jniRegisterNativeMethods(env, "com/android/ravenwood/RavenwoodRuntimeNative",
-            sMethods, NELEM(sMethods));
+    jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
     if (res < 0) {
         return res;
     }
diff --git a/ravenwood/runtime-jni/ravenwood_sysprop.cpp b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
new file mode 100644
index 0000000..4fb61b6
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#include <dlfcn.h>
+
+#include <set>
+
+#include "jni_helper.h"
+
+// Implement a rudimentary system properties data store
+
+#define PROP_VALUE_MAX 92
+
+namespace {
+
+struct prop_info {
+    std::string key;
+    mutable std::string value;
+    mutable uint32_t serial;
+
+    prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {}
+};
+
+struct prop_info_cmp {
+    using is_transparent = void;
+    bool operator()(const prop_info& lhs, const prop_info& rhs) {
+        return lhs.key < rhs.key;
+    }
+    bool operator()(std::string_view lhs, const prop_info& rhs) {
+        return lhs < rhs.key;
+    }
+    bool operator()(const prop_info& lhs, std::string_view rhs) {
+        return lhs.key < rhs;
+    }
+};
+
+} // namespace
+
+static auto& g_properties_lock = *new std::mutex;
+static auto& g_properties = *new std::set<prop_info, prop_info_cmp>;
+
+static bool property_set(const char* key, const char* value) {
+    if (key == nullptr || *key == '\0') return false;
+    if (value == nullptr) value = "";
+    bool read_only = !strncmp(key, "ro.", 3);
+    if (!read_only && strlen(value) >= PROP_VALUE_MAX) return -1;
+
+    std::lock_guard lock(g_properties_lock);
+    auto [it, success] = g_properties.emplace(key, value);
+    if (read_only) return success;
+    if (!success) {
+        it->value = value;
+        ++it->serial;
+    }
+    return true;
+}
+
+template <typename Func>
+static void property_get(const char* key, Func callback) {
+    std::lock_guard lock(g_properties_lock);
+    auto it = g_properties.find(key);
+    if (it != g_properties.end()) {
+        callback(*it);
+    }
+}
+
+// Redefine the __system_property_XXX functions here so we can perform
+// logging and access checks for all sysprops in native code.
+
+static void check_system_property_access(const char* key, bool write);
+
+extern "C" {
+
+int __system_property_set(const char* key, const char* value) {
+    check_system_property_access(key, true);
+    return property_set(key, value) ? 0 : -1;
+}
+
+int __system_property_get(const char* key, char* value) {
+    check_system_property_access(key, false);
+    *value = '\0';
+    property_get(key, [&](const prop_info& info) {
+        snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str());
+    });
+    return strlen(value);
+}
+
+const prop_info* __system_property_find(const char* key) {
+    check_system_property_access(key, false);
+    const prop_info* pi = nullptr;
+    property_get(key, [&](const prop_info& info) { pi = &info; });
+    return pi;
+}
+
+void __system_property_read_callback(const prop_info* pi,
+                                     void (*callback)(void*, const char*, const char*, uint32_t),
+                                     void* cookie) {
+    std::lock_guard lock(g_properties_lock);
+    callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial);
+}
+
+} // extern "C"
+
+// ---- JNI ----
+
+static JavaVM* gVM = nullptr;
+static jclass gEnvController = nullptr;
+static jmethodID gCheckSystemPropertyAccess;
+
+static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
+    ScopedUtfChars path(env, javaPath);
+    // Force reload ourselves as global
+    dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD);
+}
+
+// Call back into Java code to check property access
+static void check_system_property_access(const char* key, bool write) {
+    if (gVM != nullptr && gEnvController != nullptr) {
+        JNIEnv* env;
+        if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
+            ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
+            env->CallStaticVoidMethod(gEnvController, gCheckSystemPropertyAccess,
+                                      env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
+            return;
+        }
+    }
+    // Not on JVM thread, abort
+    LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key);
+}
+
+static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
+    ScopedUtfChars key(env, javaKey);
+    jstring value = nullptr;
+    property_get(key.c_str(),
+                 [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); });
+    return value;
+}
+
+static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) {
+    ScopedUtfChars key(env, javaKey);
+    ScopedUtfChars value(env, javaValue);
+    return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
+}
+
+static void clearSystemProperties(JNIEnv*, jclass) {
+    std::lock_guard lock(g_properties_lock);
+    g_properties.clear();
+}
+
+static const JNINativeMethod sMethods[] = {
+        {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
+        {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
+        {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
+        {"clearSystemProperties", "()V", (void*)clearSystemProperties},
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+    ALOGI("%s: JNI_OnLoad", __FILE__);
+
+    JNIEnv* env = GetJNIEnvOrDie(vm);
+    gVM = vm;
+
+    // Fetch several references for future use
+    gEnvController = FindGlobalClassOrDie(env, kRuntimeEnvController);
+    gCheckSystemPropertyAccess =
+            GetStaticMethodIDOrDie(env, gEnvController, "checkSystemPropertyAccess",
+                                   "(Ljava/lang/String;Z)V");
+
+    // Expose raw property methods as JNI methods
+    jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
+    if (res < 0) return -1;
+
+    return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/tests/bivalentinst/Android.bp b/ravenwood/tests/bivalentinst/Android.bp
index 38d1b299..41e45e5 100644
--- a/ravenwood/tests/bivalentinst/Android.bp
+++ b/ravenwood/tests/bivalentinst/Android.bp
@@ -27,8 +27,7 @@
         "junit",
         "truth",
     ],
-    // TODO(b/366246777) uncomment it and the test.
-    // resource_apk: "RavenwoodBivalentInstTest_self_inst_device",
+    resource_apk: "RavenwoodBivalentInstTest_self_inst_device",
     auto_gen_config: true,
 }
 
@@ -53,8 +52,8 @@
         "junit",
         "truth",
     ],
-    // TODO(b/366246777) uncomment it and the test.
-    // resource_apk: "RavenwoodBivalentInstTestTarget",
+    resource_apk: "RavenwoodBivalentInstTestTarget",
+    inst_resource_apk: "RavenwoodBivalentInstTest_nonself_inst_device",
     auto_gen_config: true,
 }
 
diff --git a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java
index 9f3ca6f..92d43d7 100644
--- a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java
+++ b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java
@@ -19,7 +19,6 @@
 
 import android.app.Instrumentation;
 import android.content.Context;
-import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodConfig;
 import android.platform.test.ravenwood.RavenwoodConfig.Config;
 
@@ -97,7 +96,6 @@
     }
 
     @Test
-    @DisabledOnRavenwood(reason = "b/366246777")
     public void testTargetAppResource() {
         assertThat(sTargetContext.getString(
                 com.android.ravenwood.bivalentinst_target_app.R.string.test_string_in_target))
@@ -105,8 +103,6 @@
     }
 
     @Test
-    @DisabledOnRavenwood(
-            reason = "Loading resources from non-self-instrumenting test APK isn't supported yet")
     public void testTestAppResource() {
         assertThat(sTestContext.getString(
                 com.android.ravenwood.bivalentinsttest_nonself_inst.R.string.test_string_in_test))
diff --git a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java
index fdff222..2f35923 100644
--- a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java
+++ b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java
@@ -19,7 +19,6 @@
 
 import android.app.Instrumentation;
 import android.content.Context;
-import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodConfig;
 import android.platform.test.ravenwood.RavenwoodConfig.Config;
 
@@ -109,7 +108,6 @@
     }
 
     @Test
-    @DisabledOnRavenwood(reason = "b/366246777")
     public void testTargetAppResource() {
         assertThat(sTargetContext.getString(
                 com.android.ravenwood.bivalentinsttest_self_inst.R.string.test_string_in_test))
@@ -117,7 +115,6 @@
     }
 
     @Test
-    @DisabledOnRavenwood(reason = "b/366246777")
     public void testTestAppResource() {
         assertThat(sTestContext.getString(
                 com.android.ravenwood.bivalentinsttest_self_inst.R.string.test_string_in_test))
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
index 09ed12d..bd01313 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
@@ -33,6 +33,7 @@
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
 
 import java.util.ArrayList;
@@ -365,14 +366,14 @@
     @Expected("""
     testRunStarted: classes
     testSuiteStarted: classes
-    testSuiteStarted: ClassUnloadbleTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleTest)
-    testIgnored: ClassUnloadbleTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleTest)
-    testSuiteFinished: ClassUnloadbleTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleTest)
+    testSuiteStarted: ClassUnloadbleAndDisabledTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndDisabledTest)
+    testIgnored: ClassUnloadbleAndDisabledTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndDisabledTest)
+    testSuiteFinished: ClassUnloadbleAndDisabledTest(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndDisabledTest)
     testSuiteFinished: classes
     testRunFinished: 0,0,0,1
     """)
     // CHECKSTYLE:ON
-    public static class ClassUnloadbleTest {
+    public static class ClassUnloadbleAndDisabledTest {
         static {
             Assert.fail("Class unloadable!");
         }
@@ -385,4 +386,73 @@
         public void test2() {
         }
     }
+
+    /**
+     * The test class is unloadable, but has a @DisabledOnRavenwood.
+     */
+    @RunWith(AndroidJUnit4.class)
+    // CHECKSTYLE:OFF
+    @Expected("""
+    testRunStarted: classes
+    testSuiteStarted: classes
+    testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndEnabledTest
+    testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndEnabledTest
+    testStarted: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndEnabledTest)
+    testFailure: Class unloadable!
+    testFinished: test1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndEnabledTest)
+    testStarted: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndEnabledTest)
+    testFailure: Class unloadable!
+    testFinished: test2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ClassUnloadbleAndEnabledTest)
+    testSuiteFinished: classes
+    testRunFinished: 2,2,0,0
+    """)
+    // CHECKSTYLE:ON
+    public static class ClassUnloadbleAndEnabledTest {
+        static {
+            Assert.fail("Class unloadable!");
+        }
+
+        @Test
+        public void test1() {
+        }
+
+        @Test
+        public void test2() {
+        }
+    }
+
+    public static class BrokenTestRunner extends BlockJUnit4ClassRunner {
+        public BrokenTestRunner(Class<?> testClass) throws InitializationError {
+            super(testClass);
+
+            if (true)  {
+                throw new RuntimeException("This is a broken test runner!");
+            }
+        }
+    }
+
+    /**
+     * The test runner throws an exception from the ctor.
+     */
+    @RunWith(BrokenTestRunner.class)
+    // CHECKSTYLE:OFF
+    @Expected("""
+    testRunStarted: classes
+    testSuiteStarted: classes
+    testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
+    testFailure: Exception detected in constructor
+    testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
+    testSuiteFinished: classes
+    testRunFinished: 1,1,0,0
+    """)
+    // CHECKSTYLE:ON
+    public static class BrokenRunnerTest {
+        @Test
+        public void test1() {
+        }
+
+        @Test
+        public void test2() {
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 3224b27..73b7b35 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -165,16 +165,27 @@
     protected final AccessibilitySecurityPolicy mSecurityPolicy;
     protected final AccessibilityTrace mTrace;
 
-    // The attribution tag set by the service that is bound to this instance
+    /** The attribution tag set by the client that is bound to this instance */
     protected String mAttributionTag;
 
     protected int mDisplayTypes = DISPLAY_TYPE_DEFAULT;
 
-    // The service that's bound to this instance. Whenever this value is non-null, this
-    // object is registered as a death recipient
-    IBinder mService;
+    /**
+     * Binder of the {@link #mClient}.
+     *
+     * <p>Whenever this value is non-null, it should be registered as a {@link
+     * IBinder.DeathRecipient}
+     */
+    @Nullable IBinder mClientBinder;
 
-    IAccessibilityServiceClient mServiceInterface;
+    /**
+     * The accessibility client this class represents.
+     *
+     * <p>The client is in the application process, i.e., it's a client of system_server. Depending
+     * on the use case, the client can be an {@link AccessibilityService}, a {@code UiAutomation},
+     * etc.
+     */
+    @Nullable IAccessibilityServiceClient mClient;
 
     int mEventTypes;
 
@@ -218,10 +229,10 @@
     int mGenericMotionEventSources;
     int mObservedMotionEventSources;
 
-    // the events pending events to be dispatched to this service
+    /** Pending events to be dispatched to the client */
     final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>();
 
-    /** Whether this service relies on its {@link AccessibilityCache} being up to date */
+    /** Whether the client relies on its {@link AccessibilityCache} being up to date */
     boolean mUsesAccessibilityCache = false;
 
     // Handler only for dispatching accessibility events since we use event
@@ -230,7 +241,7 @@
 
     final SparseArray<IBinder> mOverlayWindowTokens = new SparseArray();
 
-    // All the embedded accessibility overlays that have been added by this service.
+    /** All the embedded accessibility overlays that have been added by the client. */
     private List<SurfaceControl> mOverlays = new ArrayList<>();
 
     /** The timestamp of requesting to take screenshot in milliseconds */
@@ -274,7 +285,8 @@
 
         /**
          * Called back to notify system that the client has changed
-         * @param serviceInfoChanged True if the service's AccessibilityServiceInfo changed.
+         *
+         * @param serviceInfoChanged True if the client's AccessibilityServiceInfo changed.
          */
         void onClientChangeLocked(boolean serviceInfoChanged);
 
@@ -360,21 +372,22 @@
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mIPlatformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
-        mEventDispatchHandler = new Handler(mainHandler.getLooper()) {
-            @Override
-            public void handleMessage(Message message) {
-                final int eventType =  message.what;
-                AccessibilityEvent event = (AccessibilityEvent) message.obj;
-                boolean serviceWantsEvent = message.arg1 != 0;
-                notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent);
-            }
-        };
+        mEventDispatchHandler =
+                new Handler(mainHandler.getLooper()) {
+                    @Override
+                    public void handleMessage(Message message) {
+                        final int eventType = message.what;
+                        AccessibilityEvent event = (AccessibilityEvent) message.obj;
+                        boolean clientWantsEvent = message.arg1 != 0;
+                        notifyAccessibilityEventInternal(eventType, event, clientWantsEvent);
+                    }
+                };
         setDynamicallyConfigurableProperties(accessibilityServiceInfo);
     }
 
     @Override
     public boolean onKeyEvent(KeyEvent keyEvent, int sequenceNumber) {
-        if (!mRequestFilterKeyEvents || (mServiceInterface == null)) {
+        if (!mRequestFilterKeyEvents || (mClient == null)) {
             return false;
         }
         if((mAccessibilityServiceInfo.getCapabilities()
@@ -388,7 +401,7 @@
             if (svcClientTracingEnabled()) {
                 logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber);
             }
-            mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
+            mClient.onKeyEvent(keyEvent, sequenceNumber);
         } catch (RemoteException e) {
             return false;
         }
@@ -470,7 +483,7 @@
     }
 
     public boolean canReceiveEventsLocked() {
-        return (mEventTypes != 0 && mService != null);
+        return (mEventTypes != 0 && mClientBinder != null);
     }
 
     @RequiresNoPermission
@@ -520,7 +533,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                // If the XML manifest had data to configure the service its info
+                // If the XML manifest had data to configure the AccessibilityService, its info
                 // should be already set. In such a case update only the dynamically
                 // configurable properties.
                 boolean oldRequestIme = mRequestImeApis;
@@ -1733,40 +1746,40 @@
         try {
             // Clear the proxy in the other process so this
             // IAccessibilityServiceConnection can be garbage collected.
-            if (mServiceInterface != null) {
+            if (mClient != null) {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("init", "null, " + mId + ", null");
                 }
-                mServiceInterface.init(null, mId, null);
+                mClient.init(null, mId, null);
             }
         } catch (RemoteException re) {
                 /* ignore */
         }
-        if (mService != null) {
+        if (mClientBinder != null) {
             try {
-                mService.unlinkToDeath(this, 0);
+                mClientBinder.unlinkToDeath(this, 0);
             } catch (NoSuchElementException e) {
                 Slog.e(LOG_TAG, "Failed unregistering death link");
             }
-            mService = null;
+            mClientBinder = null;
         }
 
-        mServiceInterface = null;
+        mClient = null;
         mReceivedAccessibilityButtonCallbackSinceBind = false;
     }
 
     public boolean isConnectedLocked() {
-        return (mService != null);
+        return (mClientBinder != null);
     }
 
     public void notifyAccessibilityEvent(AccessibilityEvent event) {
         synchronized (mLock) {
             final int eventType = event.getEventType();
 
-            final boolean serviceWantsEvent = wantsEventLocked(event);
+            final boolean clientWantsEvent = clientWantsEventLocked(event);
             final boolean requiredForCacheConsistency = mUsesAccessibilityCache
                     && ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & eventType) != 0);
-            if (!serviceWantsEvent && !requiredForCacheConsistency) {
+            if (!clientWantsEvent && !requiredForCacheConsistency) {
                 return;
             }
 
@@ -1774,7 +1787,7 @@
                 return;
             }
             // Make a copy since during dispatch it is possible the event to
-            // be modified to remove its source if the receiving service does
+            // be modified to remove its source if the receiving client does
             // not have permission to access the window content.
             AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
             Message message;
@@ -1792,22 +1805,20 @@
                 // Send all messages, bypassing mPendingEvents
                 message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
             }
-            message.arg1 = serviceWantsEvent ? 1 : 0;
+            message.arg1 = clientWantsEvent ? 1 : 0;
 
             mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
         }
     }
 
     /**
-     * Determines if given event can be dispatched to a service based on the package of the
-     * event source. Specifically, a service is notified if it is interested in events from the
-     * package.
+     * Determines if given event can be dispatched to a client based on the package of the event
+     * source. Specifically, a client is notified if it is interested in events from the package.
      *
      * @param event The event.
-     * @return True if the listener should be notified, false otherwise.
+     * @return True if the client should be notified, false otherwise.
      */
-    private boolean wantsEventLocked(AccessibilityEvent event) {
-
+    private boolean clientWantsEventLocked(AccessibilityEvent event) {
         if (!canReceiveEventsLocked()) {
             return false;
         }
@@ -1838,22 +1849,20 @@
     }
 
     /**
-     * Notifies an accessibility service client for a scheduled event given the event type.
+     * Notifies a client for a scheduled event given the event type.
      *
      * @param eventType The type of the event to dispatch.
      */
     private void notifyAccessibilityEventInternal(
-            int eventType,
-            AccessibilityEvent event,
-            boolean serviceWantsEvent) {
-        IAccessibilityServiceClient listener;
+            int eventType, AccessibilityEvent event, boolean clientWantsEvent) {
+        IAccessibilityServiceClient client;
 
         synchronized (mLock) {
-            listener = mServiceInterface;
+            client = mClient;
 
-            // If the service died/was disabled while the message for dispatching
-            // the accessibility event was propagating the listener may be null.
-            if (listener == null) {
+            // If the client (in the application process) died/was disabled while the message for
+            // dispatching the accessibility event was propagating, "client" may be null.
+            if (client == null) {
                 return;
             }
 
@@ -1868,7 +1877,7 @@
                 //   1) A binder thread calls notifyAccessibilityServiceDelayedLocked
                 //      which posts a message for dispatching an event and stores the event
                 //      in mPendingEvents.
-                //   2) The message is pulled from the queue by the handler on the service
+                //   2) The message is pulled from the queue by the handler on the client
                 //      thread and this method is just about to acquire the lock.
                 //   3) Another binder thread acquires the lock in notifyAccessibilityEvent
                 //   4) notifyAccessibilityEvent recycles the event that this method was about
@@ -1876,7 +1885,7 @@
                 //   5) This method grabs the new event, processes it, and removes it from
                 //      mPendingEvents
                 //   6) The second message dispatched in (4) arrives, but the event has been
-                //      remvoved in (5).
+                //      removed in (5).
                 event = mPendingEvents.get(eventType);
                 if (event == null) {
                     return;
@@ -1893,14 +1902,14 @@
 
         try {
             if (svcClientTracingEnabled()) {
-                logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent);
+                logTraceSvcClient("onAccessibilityEvent", event + ";" + clientWantsEvent);
             }
-            listener.onAccessibilityEvent(event, serviceWantsEvent);
+            client.onAccessibilityEvent(event, clientWantsEvent);
             if (DEBUG) {
-                Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
+                Slog.i(LOG_TAG, "Event " + event + " sent to " + client);
             }
         } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
+            Slog.e(LOG_TAG, "Error during sending " + event + " to " + client, re);
         } finally {
             event.recycle();
         }
@@ -1978,122 +1987,126 @@
         return (mGenericMotionEventSources & eventSourceWithoutClass) != 0;
     }
 
-
     /**
-     * Called by the invocation handler to notify the service that the
-     * state of magnification has changed.
+     * Called by the invocation handler to notify the client that the state of magnification has
+     * changed.
      */
-    private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
-            @NonNull MagnificationConfig config) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+    private void notifyMagnificationChangedInternal(
+            int displayId, @NonNull Region region, @NonNull MagnificationConfig config) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
                             + config.toString());
                 }
-                listener.onMagnificationChanged(displayId, region, config);
+                client.onMagnificationChanged(displayId, region, config);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
+                Slog.e(LOG_TAG, "Error sending magnification changes to " + mClientBinder, re);
             }
         }
     }
 
     /**
-     * Called by the invocation handler to notify the service that the state of the soft
-     * keyboard show mode has changed.
+     * Called by the invocation handler to notify the client that the state of the soft keyboard
+     * show mode has changed.
      */
     private void notifySoftKeyboardShowModeChangedInternal(int showState) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState));
                 }
-                listener.onSoftKeyboardShowModeChanged(showState);
+                client.onSoftKeyboardShowModeChanged(showState);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error sending soft keyboard show mode changes to " + mService,
+                Slog.e(
+                        LOG_TAG,
+                        "Error sending soft keyboard show mode changes to " + mClientBinder,
                         re);
             }
         }
     }
 
     private void notifyAccessibilityButtonClickedInternal(int displayId) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId));
                 }
-                listener.onAccessibilityButtonClicked(displayId);
+                client.onAccessibilityButtonClicked(displayId);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error sending accessibility button click to " + mService, re);
+                Slog.e(LOG_TAG, "Error sending accessibility button click to " + mClientBinder, re);
             }
         }
     }
 
     private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) {
-        // Only notify the service if it's not been notified or the state has changed
+        // Only notify the client if it's not been notified or the state has changed
         if (mReceivedAccessibilityButtonCallbackSinceBind
                 && (mLastAccessibilityButtonCallbackState == available)) {
             return;
         }
         mReceivedAccessibilityButtonCallbackSinceBind = true;
         mLastAccessibilityButtonCallbackState = available;
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("onAccessibilityButtonAvailabilityChanged",
                             String.valueOf(available));
                 }
-                listener.onAccessibilityButtonAvailabilityChanged(available);
+                client.onAccessibilityButtonAvailabilityChanged(available);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG,
-                        "Error sending accessibility button availability change to " + mService,
+                Slog.e(
+                        LOG_TAG,
+                        "Error sending accessibility button availability change to "
+                                + mClientBinder,
                         re);
             }
         }
     }
 
     private void notifyGestureInternal(AccessibilityGestureEvent gestureInfo) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("onGesture", gestureInfo.toString());
                 }
-                listener.onGesture(gestureInfo);
+                client.onGesture(gestureInfo);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error during sending gesture " + gestureInfo
-                        + " to " + mService, re);
-            }
-        }
-    }
-
-    private void notifySystemActionsChangedInternal() {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
-            try {
-                if (svcClientTracingEnabled()) {
-                    logTraceSvcClient("onSystemActionsChanged", "");
-                }
-                listener.onSystemActionsChanged();
-            } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error sending system actions change to " + mService,
+                Slog.e(
+                        LOG_TAG,
+                        "Error during sending gesture " + gestureInfo + " to " + mClientBinder,
                         re);
             }
         }
     }
 
+    private void notifySystemActionsChangedInternal() {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
+            try {
+                if (svcClientTracingEnabled()) {
+                    logTraceSvcClient("onSystemActionsChanged", "");
+                }
+                client.onSystemActionsChanged();
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error sending system actions change to " + mClientBinder, re);
+            }
+        }
+    }
+
     private void notifyClearAccessibilityCacheInternal() {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("clearAccessibilityCache", "");
                 }
-                listener.clearAccessibilityCache();
+                client.clearAccessibilityCache();
             } catch (RemoteException re) {
                 Slog.e(LOG_TAG, "Error during requesting accessibility info cache"
                         + " to be cleared.", re);
@@ -2106,70 +2119,66 @@
 
     private void setImeSessionEnabledInternal(IAccessibilityInputMethodSession session,
             boolean enabled) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null && session != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null && session != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("createImeSession", "");
                 }
-                listener.setImeSessionEnabled(session, enabled);
+                client.setImeSessionEnabled(session, enabled);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG,
-                        "Error requesting IME session from " + mService, re);
+                Slog.e(LOG_TAG, "Error requesting IME session from " + mClientBinder, re);
             }
         }
     }
 
     private void bindInputInternal() {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("bindInput", "");
                 }
-                listener.bindInput();
+                client.bindInput();
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG,
-                        "Error binding input to " + mService, re);
+                Slog.e(LOG_TAG, "Error binding input to " + mClientBinder, re);
             }
         }
     }
 
     private void unbindInputInternal() {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("unbindInput", "");
                 }
-                listener.unbindInput();
+                client.unbindInput();
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG,
-                        "Error unbinding input to " + mService, re);
+                Slog.e(LOG_TAG, "Error unbinding input to " + mClientBinder, re);
             }
         }
     }
 
     private void startInputInternal(IRemoteAccessibilityInputConnection connection,
             EditorInfo editorInfo, boolean restarting) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("startInput", "editorInfo=" + editorInfo
                             + " restarting=" + restarting);
                 }
-                listener.startInput(connection, editorInfo, restarting);
+                client.startInput(connection, editorInfo, restarting);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG,
-                        "Error starting input to " + mService, re);
+                Slog.e(LOG_TAG, "Error starting input to " + mClientBinder, re);
             }
         }
     }
 
-    protected IAccessibilityServiceClient getServiceInterfaceSafely() {
+    protected IAccessibilityServiceClient getClientSafely() {
         synchronized (mLock) {
-            return mServiceInterface;
+            return mClient;
         }
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7580b69..d595d02 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1435,8 +1435,8 @@
                 interfacesToInterrupt = new ArrayList<>(services.size());
                 for (int i = 0; i < services.size(); i++) {
                     AccessibilityServiceConnection service = services.get(i);
-                    IBinder a11yServiceBinder = service.mService;
-                    IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
+                    IBinder a11yServiceBinder = service.mClientBinder;
+                    IAccessibilityServiceClient a11yServiceInterface = service.mClient;
                     if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
                         interfacesToInterrupt.add(a11yServiceInterface);
                     }
@@ -4962,9 +4962,14 @@
                 if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
                         && android.security.Flags.extendEcmToAllSettings()) {
                     try {
-                        return !mContext.getSystemService(EnhancedConfirmationManager.class)
-                                .isRestricted(packageName,
-                                        AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+                        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;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 786d167..15999d1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -166,8 +166,9 @@
             if (userState.getBindInstantServiceAllowedLocked()) {
                 flags |= Context.BIND_ALLOW_INSTANT;
             }
-            if (mService == null && mContext.bindServiceAsUser(
-                    mIntent, this, flags, new UserHandle(userState.mUserId))) {
+            if (mClientBinder == null
+                    && mContext.bindServiceAsUser(
+                            mIntent, this, flags, new UserHandle(userState.mUserId))) {
                 userState.getBindingServicesLocked().add(mComponentName);
             }
         } finally {
@@ -227,20 +228,20 @@
             addWindowTokensForAllDisplays();
         }
         synchronized (mLock) {
-            if (mService != service) {
-                if (mService != null) {
-                    mService.unlinkToDeath(this, 0);
+            if (mClientBinder != service) {
+                if (mClientBinder != null) {
+                    mClientBinder.unlinkToDeath(this, 0);
                 }
-                mService = service;
+                mClientBinder = service;
                 try {
-                    mService.linkToDeath(this, 0);
+                    mClientBinder.linkToDeath(this, 0);
                 } catch (RemoteException re) {
                     Slog.e(LOG_TAG, "Failed registering death link");
                     binderDied();
                     return;
                 }
             }
-            mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
+            mClient = IAccessibilityServiceClient.Stub.asInterface(service);
             if (userState == null) return;
             userState.addServiceLocked(this);
             mSystemSupport.onClientChangeLocked(false);
@@ -261,7 +262,7 @@
     }
 
     private void initializeService() {
-        IAccessibilityServiceClient serviceInterface = null;
+        IAccessibilityServiceClient client = null;
         synchronized (mLock) {
             AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
@@ -272,18 +273,17 @@
                 bindingServices.remove(mComponentName);
                 crashedServices.remove(mComponentName);
                 mAccessibilityServiceInfo.crashed = false;
-                serviceInterface = mServiceInterface;
+                client = mClient;
             }
             // There's a chance that service is removed from enabled_accessibility_services setting
             // key, but skip unbinding because of it's in binding state. Unbinds it if it's
             // not in enabled service list.
-            if (serviceInterface != null
-                    && !userState.getEnabledServicesLocked().contains(mComponentName)) {
+            if (client != null && !userState.getEnabledServicesLocked().contains(mComponentName)) {
                 mSystemSupport.onClientChangeLocked(false);
                 return;
             }
         }
-        if (serviceInterface == null) {
+        if (client == null) {
             binderDied();
             return;
         }
@@ -292,10 +292,9 @@
                 logTraceSvcClient("init",
                         this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
             }
-            serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+            client.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
         } catch (RemoteException re) {
-            Slog.w(LOG_TAG, "Error while setting connection for service: "
-                    + serviceInterface, re);
+            Slog.w(LOG_TAG, "Error while setting connection for service: " + client, re);
             binderDied();
         }
     }
@@ -496,7 +495,7 @@
 
     @Override
     public boolean isCapturingFingerprintGestures() {
-        return (mServiceInterface != null)
+        return (mClient != null)
                 && mSecurityPolicy.canCaptureFingerprintGestures(this)
                 && mCaptureFingerprintGestures;
     }
@@ -506,17 +505,17 @@
         if (!isCapturingFingerprintGestures()) {
             return;
         }
-        IAccessibilityServiceClient serviceInterface;
+        IAccessibilityServiceClient client;
         synchronized (mLock) {
-            serviceInterface = mServiceInterface;
+            client = mClient;
         }
-        if (serviceInterface != null) {
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient(
                             "onFingerprintCapturingGesturesChanged", String.valueOf(active));
                 }
-                mServiceInterface.onFingerprintCapturingGesturesChanged(active);
+                mClient.onFingerprintCapturingGesturesChanged(active);
             } catch (RemoteException e) {
             }
         }
@@ -527,16 +526,16 @@
         if (!isCapturingFingerprintGestures()) {
             return;
         }
-        IAccessibilityServiceClient serviceInterface;
+        IAccessibilityServiceClient client;
         synchronized (mLock) {
-            serviceInterface = mServiceInterface;
+            client = mClient;
         }
-        if (serviceInterface != null) {
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture));
                 }
-                mServiceInterface.onFingerprintGesture(gesture);
+                mClient.onFingerprintGesture(gesture);
             } catch (RemoteException e) {
             }
         }
@@ -546,7 +545,7 @@
     @Override
     public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
         synchronized (mLock) {
-            if (mServiceInterface != null && mSecurityPolicy.canPerformGestures(this)) {
+            if (mClient != null && mSecurityPolicy.canPerformGestures(this)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     MotionEventInjector motionEventInjector =
@@ -557,16 +556,18 @@
                     if (motionEventInjector != null
                             && mWindowManagerService.isTouchOrFaketouchDevice()) {
                         motionEventInjector.injectEvents(
-                                gestureSteps.getList(), mServiceInterface, sequence, displayId);
+                                gestureSteps.getList(), mClient, sequence, displayId);
                     } else {
                         try {
                             if (svcClientTracingEnabled()) {
                                 logTraceSvcClient("onPerformGestureResult", sequence + ", false");
                             }
-                            mServiceInterface.onPerformGestureResult(sequence, false);
+                            mClient.onPerformGestureResult(sequence, false);
                         } catch (RemoteException re) {
-                            Slog.e(LOG_TAG, "Error sending motion event injection failure to "
-                                    + mServiceInterface, re);
+                            Slog.e(
+                                    LOG_TAG,
+                                    "Error sending motion event injection failure to " + mClient,
+                                    re);
                         }
                     }
                 } finally {
@@ -631,48 +632,47 @@
 
     @Override
     protected void createImeSessionInternal() {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (svcClientTracingEnabled()) {
                     logTraceSvcClient("createImeSession", "");
                 }
                 AccessibilityInputMethodSessionCallback
                         callback = new AccessibilityInputMethodSessionCallback(mUserId);
-                listener.createImeSession(callback);
+                client.createImeSession(callback);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG,
-                        "Error requesting IME session from " + mService, re);
+                Slog.e(LOG_TAG, "Error requesting IME session from " + mClientBinder, re);
             }
         }
     }
 
     private void notifyMotionEventInternal(MotionEvent event) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (mTrace.isA11yTracingEnabled()) {
                     logTraceSvcClient(".onMotionEvent ",
                             event.toString());
                 }
-                listener.onMotionEvent(event);
+                client.onMotionEvent(event);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
+                Slog.e(LOG_TAG, "Error sending motion event to" + mClientBinder, re);
             }
         }
     }
 
     private void notifyTouchStateInternal(int displayId, int state) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
+        final IAccessibilityServiceClient client = getClientSafely();
+        if (client != null) {
             try {
                 if (mTrace.isA11yTracingEnabled()) {
                     logTraceSvcClient(".onTouchStateChanged ",
                             TouchInteractionController.stateToString(state));
                 }
-                listener.onTouchStateChanged(displayId, state);
+                client.onTouchStateChanged(displayId, state);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error sending motion event to" + mService, re);
+                Slog.e(LOG_TAG, "Error sending motion event to" + mClientBinder, re);
             }
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 54368ca..4b97745 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -73,12 +73,16 @@
 
     private static final int MESSAGE_MOVE_MOUSE_POINTER = 1;
     private static final int MESSAGE_SCROLL_MOUSE_POINTER = 2;
-    private static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f;
     private static final int KEY_NOT_SET = -1;
 
     /** Time interval after which mouse action will be repeated */
     private static final int INTERVAL_MILLIS = 10;
 
+    @VisibleForTesting
+    public static final float MOUSE_POINTER_MOVEMENT_STEP = 1.8f;
+    @VisibleForTesting
+    public static final float MOUSE_SCROLL_STEP = 0.2f;
+
     private final AccessibilityManagerService mAms;
     private final Handler mHandler;
     private final InputManager mInputManager;
@@ -281,8 +285,8 @@
         MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
                 keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
         float y = switch (mouseKeyEvent) {
-            case UP_MOVE_OR_SCROLL -> 1.0f;
-            case DOWN_MOVE_OR_SCROLL -> -1.0f;
+            case UP_MOVE_OR_SCROLL -> MOUSE_SCROLL_STEP;
+            case DOWN_MOVE_OR_SCROLL -> -MOUSE_SCROLL_STEP;
             default -> 0.0f;
         };
         waitForVirtualMouseCreation();
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index 4cb3d24..cd97d83 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -109,14 +109,11 @@
         return mDeviceId;
     }
 
-    /**
-     * Called when the proxy is registered.
-     */
-    void initializeServiceInterface(IAccessibilityServiceClient serviceInterface)
-            throws RemoteException {
-        mServiceInterface = serviceInterface;
-        mService = serviceInterface.asBinder();
-        mServiceInterface.init(this, mId, this.mOverlayWindowTokens.get(mDisplayId));
+    /** Called when the proxy is registered. */
+    void initializeClient(IAccessibilityServiceClient client) throws RemoteException {
+        mClient = client;
+        mClientBinder = client.asBinder();
+        mClient.init(this, mId, this.mOverlayWindowTokens.get(mDisplayId));
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index b4deeb0..da11a76 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -214,7 +214,7 @@
                 mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId);
             }
         });
-        connection.initializeServiceInterface(client);
+        connection.initializeClient(client);
     }
 
     private void registerVirtualDeviceListener() {
@@ -561,8 +561,8 @@
             final ProxyAccessibilityServiceConnection proxy =
                     mProxyA11yServiceConnections.valueAt(i);
             if (proxy != null && proxy.getDeviceId() == deviceId) {
-                final IBinder proxyBinder = proxy.mService;
-                final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface;
+                final IBinder proxyBinder = proxy.mClientBinder;
+                final IAccessibilityServiceClient proxyInterface = proxy.mClient;
                 if ((proxyBinder != null) && (proxyInterface != null)) {
                     interfaces.add(proxyInterface);
                 }
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index f85d786..ed4eeb5 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -107,8 +107,7 @@
                 Binder.getCallingUserHandle().getIdentifier());
         if (mUiAutomationService != null) {
             throw new IllegalStateException(
-                    "UiAutomationService " + mUiAutomationService.mServiceInterface
-                            + "already registered!");
+                    "UiAutomationService " + mUiAutomationService.mClient + "already registered!");
         }
 
         try {
@@ -130,10 +129,9 @@
                 mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
                 systemActionPerformer, awm);
         mUiAutomationServiceOwner = owner;
-        mUiAutomationService.mServiceInterface = serviceClient;
+        mUiAutomationService.mClient = serviceClient;
         try {
-            mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
-                    0);
+            mUiAutomationService.mClient.asBinder().linkToDeath(mUiAutomationService, 0);
         } catch (RemoteException re) {
             Slog.e(LOG_TAG, "Failed registering death link: " + re);
             destroyUiAutomationService();
@@ -149,10 +147,10 @@
         synchronized (mLock) {
             if (useAccessibility()
                     && ((mUiAutomationService == null)
-                    || (serviceClient == null)
-                    || (mUiAutomationService.mServiceInterface == null)
-                    || (serviceClient.asBinder()
-                    != mUiAutomationService.mServiceInterface.asBinder()))) {
+                            || (serviceClient == null)
+                            || (mUiAutomationService.mClient == null)
+                            || (serviceClient.asBinder()
+                                    != mUiAutomationService.mClient.asBinder()))) {
                 throw new IllegalStateException("UiAutomationService " + serviceClient
                         + " not registered!");
             }
@@ -230,8 +228,7 @@
     private void destroyUiAutomationService() {
         synchronized (mLock) {
             if (mUiAutomationService != null) {
-                mUiAutomationService.mServiceInterface.asBinder().unlinkToDeath(
-                        mUiAutomationService, 0);
+                mUiAutomationService.mClient.asBinder().unlinkToDeath(mUiAutomationService, 0);
                 mUiAutomationService.onRemoved();
                 mUiAutomationService.resetLocked();
                 mUiAutomationService = null;
@@ -271,40 +268,48 @@
 
         void connectServiceUnknownThread() {
             // This needs to be done on the main thread
-            mMainHandler.post(() -> {
-                try {
-                    final IAccessibilityServiceClient serviceInterface;
-                    final UiAutomationService uiAutomationService;
-                    synchronized (mLock) {
-                        serviceInterface = mServiceInterface;
-                        uiAutomationService = mUiAutomationService;
-                        if (serviceInterface == null) {
-                            mService = null;
-                        } else {
-                            mService = mServiceInterface.asBinder();
-                            mService.linkToDeath(this, 0);
+            mMainHandler.post(
+                    () -> {
+                        try {
+                            final IAccessibilityServiceClient client;
+                            final UiAutomationService uiAutomationService;
+                            synchronized (mLock) {
+                                client = mClient;
+                                uiAutomationService = mUiAutomationService;
+                                if (client == null) {
+                                    mClientBinder = null;
+                                } else {
+                                    mClientBinder = mClient.asBinder();
+                                    mClientBinder.linkToDeath(this, 0);
+                                }
+                            }
+                            // If the client is null, the UiAutomation has been shut down on
+                            // another thread.
+                            if (client != null && uiAutomationService != null) {
+                                uiAutomationService.addWindowTokensForAllDisplays();
+                                if (mTrace.isA11yTracingEnabledForTypes(
+                                        AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+                                    mTrace.logTrace(
+                                            "UiAutomationService.connectServiceUnknownThread",
+                                            AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT,
+                                            "serviceConnection="
+                                                    + this
+                                                    + ";connectionId="
+                                                    + mId
+                                                    + "windowToken="
+                                                    + mOverlayWindowTokens.get(
+                                                            Display.DEFAULT_DISPLAY));
+                                }
+                                client.init(
+                                        this,
+                                        mId,
+                                        mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+                            }
+                        } catch (RemoteException re) {
+                            Slog.w(LOG_TAG, "Error initializing connection", re);
+                            destroyUiAutomationService();
                         }
-                    }
-                    // If the serviceInterface is null, the UiAutomation has been shut down on
-                    // another thread.
-                    if (serviceInterface != null && uiAutomationService != null) {
-                        uiAutomationService.addWindowTokensForAllDisplays();
-                        if (mTrace.isA11yTracingEnabledForTypes(
-                                AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
-                            mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
-                                    AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT,
-                                    "serviceConnection=" + this + ";connectionId=" + mId
-                                    + "windowToken="
-                                    + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
-                        }
-                        serviceInterface.init(this, mId,
-                                mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
-                    }
-                } catch (RemoteException re) {
-                    Slog.w(LOG_TAG, "Error initializing connection", re);
-                    destroyUiAutomationService();
-                }
-            });
+                    });
         }
 
         @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index aa57e0b..a19fddd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -68,11 +68,15 @@
 import com.android.internal.R;
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Histogram;
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.Flags;
 import com.android.server.accessibility.gestures.GestureUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * This class handles full screen magnification in response to touch events.
  *
@@ -871,6 +875,15 @@
      */
     class DetectingState implements State, Handler.Callback {
 
+        private static final Histogram HISTOGRAM_FIRST_INTERVAL =
+                new Histogram(
+                        "accessibility.value_full_triple_tap_first_interval",
+                        new Histogram.UniformOptions(25, 0, 250));
+        private static final Histogram HISTOGRAM_SECOND_INTERVAL =
+                new Histogram(
+                        "accessibility.value_full_triple_tap_second_interval",
+                        new Histogram.UniformOptions(25, 0, 250));
+
         private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
         private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
         private static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
@@ -1115,6 +1128,12 @@
             if (multitapTriggered && numTaps > 2) {
                 final boolean enabled = !isActivated();
                 mMagnificationLogger.logMagnificationTripleTap(enabled);
+
+                List<Long> intervals = intervalsOf(mDelayedEventQueue, ACTION_UP);
+                if (intervals.size() >= 2) {
+                    HISTOGRAM_FIRST_INTERVAL.logSample(intervals.get(0));
+                    HISTOGRAM_SECOND_INTERVAL.logSample(intervals.get(1));
+                }
             }
             return multitapTriggered;
         }
@@ -1144,6 +1163,10 @@
             return event != null ? event.getEventTime() : Long.MIN_VALUE;
         }
 
+        public List<Long> intervalsOf(MotionEventInfo info, int eventType) {
+            return MotionEventInfo.intervalsOf(info, eventType);
+        }
+
         public int tapCount() {
             return MotionEventInfo.countOf(mDelayedEventQueue, ACTION_UP);
         }
@@ -1649,7 +1672,7 @@
         return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(pointerDownLocation.y));
     }
 
-    private static final class MotionEventInfo {
+    public static final class MotionEventInfo {
 
         private static final int MAX_POOL_SIZE = 10;
         private static final Object sLock = new Object();
@@ -1709,6 +1732,14 @@
             }
         }
 
+        public MotionEventInfo getNext() {
+            return mNext;
+        }
+
+        public void setNext(MotionEventInfo info) {
+            mNext = info;
+        }
+
         private void clear() {
             event = recycleAndNullify(event);
             rawEvent = recycleAndNullify(rawEvent);
@@ -1721,6 +1752,23 @@
                     + countOf(info.mNext, eventType);
         }
 
+        static List<Long> intervalsOf(MotionEventInfo info, int eventType) {
+            List<Long> intervals = new ArrayList<>();
+            MotionEventInfo current = info;
+            MotionEventInfo previous = null;
+
+            while (current != null) {
+                if (current.event.getAction() == eventType) {
+                    if (previous != null) {
+                        intervals.add(current.event.getDownTime() - previous.event.getDownTime());
+                    }
+                    previous = current;
+                }
+                current = current.mNext;
+            }
+            return intervals;
+        }
+
         public static String toString(MotionEventInfo info) {
             return info == null
                     ? ""
diff --git a/services/appfunctions/Android.bp b/services/appfunctions/Android.bp
index f8ee823..eb6e468 100644
--- a/services/appfunctions/Android.bp
+++ b/services/appfunctions/Android.bp
@@ -22,4 +22,7 @@
         "java/**/*.logtags",
     ],
     libs: ["services.core"],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
index 1f98334..c3b7087 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java
@@ -16,15 +16,7 @@
 
 package com.android.server.appfunctions;
 
-import android.annotation.NonNull;
-import android.os.UserHandle;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-
 import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
@@ -41,50 +33,5 @@
                     /* unit= */ TimeUnit.SECONDS,
                     /* workQueue= */ new LinkedBlockingQueue<>());
 
-    /** A map of per-user executors for queued work. */
-    @GuardedBy("sLock")
-    private static final SparseArray<ExecutorService> mPerUserExecutorsLocked = new SparseArray<>();
-
-    private static final Object sLock = new Object();
-
-    /**
-     * Returns a per-user executor for queued metadata sync request.
-     *
-     * <p>The work submitted to these executor (Sync request) needs to be synchronous per user hence
-     * the use of a single thread.
-     *
-     * <p>Note: Use a different executor if not calling {@code submitSyncRequest} on a {@code
-     * MetadataSyncAdapter}.
-     */
-    // TODO(b/357551503): Restrict the scope of this executor to the MetadataSyncAdapter itself.
-    public static ExecutorService getPerUserSyncExecutor(@NonNull UserHandle user) {
-        synchronized (sLock) {
-            ExecutorService executor = mPerUserExecutorsLocked.get(user.getIdentifier(), null);
-            if (executor == null) {
-                executor = Executors.newSingleThreadExecutor();
-                mPerUserExecutorsLocked.put(user.getIdentifier(), executor);
-            }
-            return executor;
-        }
-    }
-
-    /**
-     * Shuts down and removes the per-user executor for queued work.
-     *
-     * <p>This should be called when the user is removed.
-     */
-    public static void shutDownAndRemoveUserExecutor(@NonNull UserHandle user)
-            throws InterruptedException {
-        ExecutorService executor;
-        synchronized (sLock) {
-            executor = mPerUserExecutorsLocked.get(user.getIdentifier());
-            mPerUserExecutorsLocked.remove(user.getIdentifier());
-        }
-        if (executor != null) {
-            executor.shutdown();
-            var unused = executor.awaitTermination(30, TimeUnit.SECONDS);
-        }
-    }
-
     private AppFunctionExecutors() {}
 }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index cf039df..082459b 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -20,11 +20,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.app.appfunctions.AppFunctionService;
 import android.app.appfunctions.AppFunctionStaticMetadataHelper;
 import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
 import android.app.appfunctions.ExecuteAppFunctionResponse;
 import android.app.appfunctions.IAppFunctionManager;
 import android.app.appfunctions.IAppFunctionService;
+import android.app.appfunctions.ICancellationCallback;
 import android.app.appfunctions.IExecuteAppFunctionCallback;
 import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback;
 import android.app.appsearch.AppSearchManager;
@@ -36,16 +39,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
 import com.android.server.SystemService.TargetUser;
 import com.android.server.appfunctions.RemoteServiceCaller.RunServiceCallCallback;
 import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
 
-import java.io.IOException;
 import java.util.Objects;
 import java.util.concurrent.CompletionException;
 
@@ -83,23 +89,6 @@
         mServiceConfig = serviceConfig;
     }
 
-    @Override
-    public void executeAppFunction(
-            @NonNull ExecuteAppFunctionAidlRequest requestInternal,
-            @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) {
-        Objects.requireNonNull(requestInternal);
-        Objects.requireNonNull(executeAppFunctionCallback);
-
-        final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback =
-                new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback);
-
-        try {
-            executeAppFunctionInternal(requestInternal, safeExecuteAppFunctionCallback);
-        } catch (Exception e) {
-            safeExecuteAppFunctionCallback.onResult(mapExceptionToExecuteAppFunctionResponse(e));
-        }
-    }
-
     /** Called when the user is unlocked. */
     public void onUserUnlocked(TargetUser user) {
         Objects.requireNonNull(user);
@@ -112,35 +101,64 @@
     public void onUserStopping(@NonNull TargetUser user) {
         Objects.requireNonNull(user);
 
-        try {
-            AppFunctionExecutors.shutDownAndRemoveUserExecutor(user.getUserHandle());
-            MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle());
-        } catch (InterruptedException e) {
-            Slog.e(TAG, "Unable to remove data for: " + user.getUserHandle(), e);
-        }
+        MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle());
     }
 
-    private void executeAppFunctionInternal(
-            ExecuteAppFunctionAidlRequest requestInternal,
-            SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
+    @Override
+    public ICancellationSignal executeAppFunction(
+            @NonNull ExecuteAppFunctionAidlRequest requestInternal,
+            @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) {
+        Objects.requireNonNull(requestInternal);
+        Objects.requireNonNull(executeAppFunctionCallback);
+
+        final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback =
+                new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback);
 
         String validatedCallingPackage;
-        UserHandle targetUser;
         try {
             validatedCallingPackage =
                     mCallerValidator.validateCallingPackage(requestInternal.getCallingPackage());
-            targetUser =
-                    mCallerValidator.verifyTargetUserHandle(
-                            requestInternal.getUserHandle(), validatedCallingPackage);
+            mCallerValidator.verifyTargetUserHandle(
+                    requestInternal.getUserHandle(), validatedCallingPackage);
         } catch (SecurityException exception) {
             safeExecuteAppFunctionCallback.onResult(
                     ExecuteAppFunctionResponse.newFailure(
                             ExecuteAppFunctionResponse.RESULT_DENIED,
                             exception.getMessage(),
                             /* extras= */ null));
-            return;
+            return null;
         }
 
+        int callingUid = Binder.getCallingUid();
+        int callingPid = Binder.getCallingPid();
+
+        ICancellationSignal localCancelTransport = CancellationSignal.createTransport();
+
+        THREAD_POOL_EXECUTOR.execute(
+                () -> {
+                    try {
+                        executeAppFunctionInternal(
+                                requestInternal,
+                                callingUid,
+                                callingPid,
+                                localCancelTransport,
+                                safeExecuteAppFunctionCallback);
+                    } catch (Exception e) {
+                        safeExecuteAppFunctionCallback.onResult(
+                                mapExceptionToExecuteAppFunctionResponse(e));
+                    }
+                });
+        return localCancelTransport;
+    }
+
+    @WorkerThread
+    private void executeAppFunctionInternal(
+            ExecuteAppFunctionAidlRequest requestInternal,
+            int callingUid,
+            int callingPid,
+            ICancellationSignal localCancelTransport,
+            SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
+        UserHandle targetUser = requestInternal.getUserHandle();
         // TODO(b/354956319): Add and honor the new enterprise policies.
         if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
             safeExecuteAppFunctionCallback.onResult(
@@ -165,7 +183,9 @@
         var unused =
                 mCallerValidator
                         .verifyCallerCanExecuteAppFunction(
-                                validatedCallingPackage,
+                                callingUid,
+                                callingPid,
+                                requestInternal.getCallingPackage(),
                                 targetPackageName,
                                 requestInternal.getClientRequest().getFunctionIdentifier())
                         .thenAccept(
@@ -191,19 +211,14 @@
                                                         /* extras= */ null));
                                         return;
                                     }
-                                    final long token = Binder.clearCallingIdentity();
-                                    try {
-                                        bindAppFunctionServiceUnchecked(
-                                                requestInternal,
-                                                serviceIntent,
-                                                targetUser,
-                                                safeExecuteAppFunctionCallback,
-                                                /* bindFlags= */ Context.BIND_AUTO_CREATE,
-                                                /* timeoutInMillis= */ mServiceConfig
-                                                        .getExecuteAppFunctionTimeoutMillis());
-                                    } finally {
-                                        Binder.restoreCallingIdentity(token);
-                                    }
+                                    bindAppFunctionServiceUnchecked(
+                                            requestInternal,
+                                            serviceIntent,
+                                            targetUser,
+                                            localCancelTransport,
+                                            safeExecuteAppFunctionCallback,
+                                            /* bindFlags= */ Context.BIND_AUTO_CREATE
+                                                    | Context.BIND_FOREGROUND_SERVICE);
                                 })
                         .exceptionally(
                                 ex -> {
@@ -217,14 +232,23 @@
             @NonNull ExecuteAppFunctionAidlRequest requestInternal,
             @NonNull Intent serviceIntent,
             @NonNull UserHandle targetUser,
+            @NonNull ICancellationSignal cancellationSignalTransport,
             @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
-            int bindFlags,
-            long timeoutInMillis) {
+            int bindFlags) {
+        CancellationSignal cancellationSignal =
+                CancellationSignal.fromTransport(cancellationSignalTransport);
+        ICancellationCallback cancellationCallback =
+                new ICancellationCallback.Stub() {
+                    @Override
+                    public void sendCancellationTransport(
+                            @NonNull ICancellationSignal cancellationTransport) {
+                        cancellationSignal.setRemote(cancellationTransport);
+                    }
+                };
         boolean bindServiceResult =
                 mRemoteServiceCaller.runServiceCall(
                         serviceIntent,
                         bindFlags,
-                        timeoutInMillis,
                         targetUser,
                         new RunServiceCallCallback<IAppFunctionService>() {
                             @Override
@@ -236,6 +260,7 @@
                                 try {
                                     service.executeAppFunction(
                                             requestInternal.getClientRequest(),
+                                            cancellationCallback,
                                             new IExecuteAppFunctionCallback.Stub() {
                                                 @Override
                                                 public void onResult(
@@ -265,16 +290,6 @@
                                                 "Failed to connect to AppFunctionService",
                                                 /* extras= */ null));
                             }
-
-                            @Override
-                            public void onTimedOut() {
-                                Slog.e(TAG, "Timed out");
-                                safeExecuteAppFunctionCallback.onResult(
-                                        ExecuteAppFunctionResponse.newFailure(
-                                                ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
-                                                "Binding to AppFunctionService timed out.",
-                                                /* extras= */ null));
-                            }
                         });
 
         if (!bindServiceResult) {
@@ -332,30 +347,27 @@
             Slog.d(TAG, "AppSearch Manager not found for user: " + user.getUserIdentifier());
             return;
         }
-        try (FutureGlobalSearchSession futureGlobalSearchSession =
+        FutureGlobalSearchSession futureGlobalSearchSession =
                 new FutureGlobalSearchSession(
-                        perUserAppSearchManager, AppFunctionExecutors.THREAD_POOL_EXECUTOR)) {
-            AppFunctionMetadataObserver appFunctionMetadataObserver =
-                    new AppFunctionMetadataObserver(
-                            user.getUserHandle(),
-                            mContext.createContextAsUser(user.getUserHandle(), /* flags= */ 0));
-            var unused =
-                    futureGlobalSearchSession
-                            .registerObserverCallbackAsync(
-                                    "android",
-                                    new ObserverSpec.Builder().build(),
-                                    THREAD_POOL_EXECUTOR,
-                                    appFunctionMetadataObserver)
-                            .whenComplete(
-                                    (voidResult, ex) -> {
-                                        if (ex != null) {
-                                            Slog.e(TAG, "Failed to register observer: ", ex);
-                                        }
-                                    });
-
-        } catch (IOException ex) {
-            Slog.e(TAG, "Failed to close observer session: ", ex);
-        }
+                        perUserAppSearchManager, AppFunctionExecutors.THREAD_POOL_EXECUTOR);
+        AppFunctionMetadataObserver appFunctionMetadataObserver =
+                new AppFunctionMetadataObserver(
+                        user.getUserHandle(),
+                        mContext.createContextAsUser(user.getUserHandle(), /* flags= */ 0));
+        var unused =
+                futureGlobalSearchSession
+                        .registerObserverCallbackAsync(
+                                "android",
+                                new ObserverSpec.Builder().build(),
+                                THREAD_POOL_EXECUTOR,
+                                appFunctionMetadataObserver)
+                        .whenComplete(
+                                (voidResult, ex) -> {
+                                    if (ex != null) {
+                                        Slog.e(TAG, "Failed to register observer: ", ex);
+                                    }
+                                    futureGlobalSearchSession.close();
+                                });
     }
 
     private void trySyncRuntimeMetadata(@NonNull TargetUser user) {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index e7a861e..3592ed5 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -60,9 +60,9 @@
      * 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 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}.
      *
      * @param callerPackageName The calling package (as previously validated).
      * @param targetPackageName The package that owns the app function to execute.
@@ -70,6 +70,8 @@
      * @return Whether the caller can execute the specified app function.
      */
     AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+            int callingUid,
+            int callingPid,
             @NonNull String callerPackageName,
             @NonNull String targetPackageName,
             @NonNull String functionId);
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index 94a63b4..8b6251a 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -20,6 +20,7 @@
 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;
 
 import android.Manifest;
@@ -41,6 +42,7 @@
 import android.os.UserManager;
 
 import com.android.internal.infra.AndroidFuture;
+
 import java.util.Objects;
 
 /* Validates that caller has the correct privilege to call an AppFunctionManager Api. */
@@ -82,7 +84,6 @@
     }
 
     @Override
-    @BinderThread
     @RequiresPermission(
             anyOf = {
                 Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
@@ -90,6 +91,8 @@
             },
             conditional = true)
     public AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+            int callingUid,
+            int callingPid,
             @NonNull String callerPackageName,
             @NonNull String targetPackageName,
             @NonNull String functionId) {
@@ -97,11 +100,11 @@
             return AndroidFuture.completedFuture(true);
         }
 
-        int pid = Binder.getCallingPid();
-        int uid = Binder.getCallingUid();
         boolean hasTrustedExecutionPermission =
                 mContext.checkPermission(
-                                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, pid, uid)
+                                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+                                callingPid,
+                                callingUid)
                         == PackageManager.PERMISSION_GRANTED;
 
         if (hasTrustedExecutionPermission) {
@@ -109,40 +112,34 @@
         }
 
         boolean hasExecutionPermission =
-                mContext.checkPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS, pid, uid)
+                mContext.checkPermission(
+                                Manifest.permission.EXECUTE_APP_FUNCTIONS, callingPid, callingUid)
                         == PackageManager.PERMISSION_GRANTED;
 
         if (!hasExecutionPermission) {
             return AndroidFuture.completedFuture(false);
         }
 
-        final long token = Binder.clearCallingIdentity();
-        try {
-            FutureAppSearchSession futureAppSearchSession =
-                    new FutureAppSearchSessionImpl(
-                            mContext.getSystemService(AppSearchManager.class),
-                            THREAD_POOL_EXECUTOR,
-                            new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
+        FutureAppSearchSession futureAppSearchSession =
+                new FutureAppSearchSessionImpl(
+                        mContext.getSystemService(AppSearchManager.class),
+                        THREAD_POOL_EXECUTOR,
+                        new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
 
-            String documentId = getDocumentIdForAppFunction(targetPackageName, functionId);
+        String documentId = getDocumentIdForAppFunction(targetPackageName, functionId);
 
-            return futureAppSearchSession
-                    .getByDocumentId(
-                            new GetByDocumentIdRequest.Builder(APP_FUNCTION_STATIC_NAMESPACE)
-                                    .addIds(documentId)
-                                    .build())
-                    .thenApply(
-                            batchResult ->
-                                    getGenericDocumentFromBatchResult(batchResult, documentId))
-                    .thenApply(
-                            CallerValidatorImpl::getRestrictCallersWithExecuteAppFunctionsProperty)
-                    .thenApply(
-                            restrictCallersWithExecuteAppFunctions ->
-                                    !restrictCallersWithExecuteAppFunctions
-                                            && hasExecutionPermission);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        return futureAppSearchSession
+                .getByDocumentId(
+                        new GetByDocumentIdRequest.Builder(APP_FUNCTION_STATIC_NAMESPACE)
+                                .addIds(documentId)
+                                .build())
+                .thenApply(
+                        batchResult -> getGenericDocumentFromBatchResult(batchResult, documentId))
+                .thenApply(document -> !getRestrictCallersWithExecuteAppFunctionsProperty(document))
+                .whenComplete(
+                        (result, throwable) -> {
+                            futureAppSearchSession.close();
+                        });
     }
 
     private static GenericDocument getGenericDocumentFromBatchResult(
@@ -167,19 +164,13 @@
     }
 
     @Override
-    @BinderThread
     public boolean isUserOrganizationManaged(@NonNull UserHandle targetUser) {
-        final long callingIdentityToken = Binder.clearCallingIdentity();
-        try {
-            if (Objects.requireNonNull(mContext.getSystemService(DevicePolicyManager.class))
-                    .isDeviceManaged()) {
-                return true;
-            }
-            return Objects.requireNonNull(mContext.getSystemService(UserManager.class))
-                    .isManagedProfile(targetUser.getIdentifier());
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentityToken);
+        if (Objects.requireNonNull(mContext.getSystemService(DevicePolicyManager.class))
+                .isDeviceManaged()) {
+            return true;
         }
+        return Objects.requireNonNull(mContext.getSystemService(UserManager.class))
+                .isManagedProfile(targetUser.getIdentifier());
     }
 
     /**
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
index 0044b4b..de2034b 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
@@ -84,6 +84,9 @@
     AndroidFuture<FutureSearchResults> search(
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec);
 
+    @Override
+    void close();
+
     /** A future API wrapper of {@link android.app.appsearch.SearchResults}. */
     interface FutureSearchResults {
 
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
index 3079d9f..d24bb87 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
@@ -38,7 +38,6 @@
 
 import com.android.internal.infra.AndroidFuture;
 
-import java.io.IOException;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -183,7 +182,15 @@
     }
 
     @Override
-    public void close() throws IOException {}
+    public void close() {
+        getSessionAsync()
+                .whenComplete(
+                        (appSearchSession, throwable) -> {
+                            if (appSearchSession != null) {
+                                appSearchSession.close();
+                            }
+                        });
+    }
 
     private static final class FutureSearchResultsImpl implements FutureSearchResults {
         private final SearchResults mSearchResults;
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
index 0c22624..874c5da 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
@@ -23,12 +23,10 @@
 import android.app.appsearch.exceptions.AppSearchException;
 import android.app.appsearch.observer.ObserverCallback;
 import android.app.appsearch.observer.ObserverSpec;
-import android.util.Slog;
 
 import com.android.internal.infra.AndroidFuture;
 
 import java.io.Closeable;
-import java.io.IOException;
 import java.util.concurrent.Executor;
 
 /** A wrapper around {@link GlobalSearchSession} that provides a future-based API. */
@@ -84,11 +82,13 @@
     }
 
     @Override
-    public void close() throws IOException {
-        try {
-            getSessionAsync().get().close();
-        } catch (Exception ex) {
-            Slog.e(TAG, "Failed to close global search session", ex);
-        }
+    public void close() {
+        getSessionAsync()
+                .whenComplete(
+                        (appSearchSession, throwable) -> {
+                            if (appSearchSession != null) {
+                                appSearchSession.close();
+                            }
+                        });
     }
 }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
index e29b6e4..d84b205 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncAdapter.java
@@ -42,6 +42,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
 import com.android.server.appfunctions.FutureAppSearchSession.FutureSearchResults;
@@ -53,7 +54,9 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 /**
  * This class implements helper methods for synchronously interacting with AppSearch while
@@ -63,9 +66,15 @@
  */
 public class MetadataSyncAdapter {
     private static final String TAG = MetadataSyncAdapter.class.getSimpleName();
-    private final Executor mSyncExecutor;
+
+    private final ExecutorService mExecutor;
+
     private final AppSearchManager mAppSearchManager;
     private final PackageManager mPackageManager;
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private Future<?> mCurrentSyncTask;
 
     // Hidden constants in {@link SetSchemaRequest} that restricts runtime metadata visibility
     // by permissions.
@@ -73,12 +82,10 @@
     public static final int EXECUTE_APP_FUNCTIONS_TRUSTED = 10;
 
     public MetadataSyncAdapter(
-            @NonNull Executor syncExecutor,
-            @NonNull PackageManager packageManager,
-            @NonNull AppSearchManager appSearchManager) {
-        mSyncExecutor = Objects.requireNonNull(syncExecutor);
+            @NonNull PackageManager packageManager, @NonNull AppSearchManager appSearchManager) {
         mPackageManager = Objects.requireNonNull(packageManager);
         mAppSearchManager = Objects.requireNonNull(appSearchManager);
+        mExecutor = Executors.newSingleThreadExecutor();
     }
 
     /**
@@ -97,7 +104,7 @@
                                 AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB)
                         .build();
         AndroidFuture<Boolean> settableSyncStatus = new AndroidFuture<>();
-        mSyncExecutor.execute(
+        Runnable runnable =
                 () -> {
                     try (FutureAppSearchSession staticMetadataSearchSession =
                                     new FutureAppSearchSessionImpl(
@@ -117,10 +124,23 @@
                     } catch (Exception ex) {
                         settableSyncStatus.completeExceptionally(ex);
                     }
-                });
+                };
+
+        synchronized (mLock) {
+            if (mCurrentSyncTask != null && !mCurrentSyncTask.isDone()) {
+                var unused = mCurrentSyncTask.cancel(false);
+            }
+            mCurrentSyncTask = mExecutor.submit(runnable);
+        }
+
         return settableSyncStatus;
     }
 
+    /** This method shuts down the {@link MetadataSyncAdapter} scheduler. */
+    public void shutDown() {
+        mExecutor.shutdown();
+    }
+
     @WorkerThread
     @VisibleForTesting
     void trySyncAppFunctionMetadataBlocking(
diff --git a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
index f421527..e933ec1 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/MetadataSyncPerUser.java
@@ -55,10 +55,7 @@
                 PackageManager perUserPackageManager = userContext.getPackageManager();
                 if (perUserAppSearchManager != null) {
                     metadataSyncAdapter =
-                            new MetadataSyncAdapter(
-                                    AppFunctionExecutors.getPerUserSyncExecutor(user),
-                                    perUserPackageManager,
-                                    perUserAppSearchManager);
+                            new MetadataSyncAdapter(perUserPackageManager, perUserAppSearchManager);
                     sPerUserMetadataSyncAdapter.put(user.getIdentifier(), metadataSyncAdapter);
                     return metadataSyncAdapter;
                 }
@@ -74,7 +71,12 @@
      */
     public static void removeUserSyncAdapter(UserHandle user) {
         synchronized (sLock) {
-            sPerUserMetadataSyncAdapter.remove(user.getIdentifier());
+            MetadataSyncAdapter metadataSyncAdapter =
+                    sPerUserMetadataSyncAdapter.get(user.getIdentifier(), null);
+            if (metadataSyncAdapter != null) {
+                metadataSyncAdapter.shutDown();
+                sPerUserMetadataSyncAdapter.remove(user.getIdentifier());
+            }
         }
     }
 }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
index 58597c3..cd5c383 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
@@ -43,7 +43,6 @@
      * @param intent An Intent object that describes the service that should be bound.
      * @param bindFlags Flags used to control the binding process See {@link
      *     android.content.Context#bindService}.
-     * @param timeoutInMillis The maximum time in milliseconds to wait for the service connection.
      * @param userHandle The UserHandle of the user for which the service should be bound.
      * @param callback A callback to be invoked for various events. See {@link
      *     RunServiceCallCallback}.
@@ -51,7 +50,6 @@
     boolean runServiceCall(
             @NonNull Intent intent,
             int bindFlags,
-            long timeoutInMillis,
             @NonNull UserHandle userHandle,
             @NonNull RunServiceCallCallback<T> callback);
 
@@ -75,11 +73,5 @@
 
         /** Called when the service connection was failed to establish. */
         void onFailedToConnect();
-
-        /**
-         * Called when the whole operation(i.e. binding and the service call) takes longer than
-         * allowed.
-         */
-        void onTimedOut();
     }
 }
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
index eea17ee..070a99d 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
@@ -62,12 +62,11 @@
     public boolean runServiceCall(
             @NonNull Intent intent,
             int bindFlags,
-            long timeoutInMillis,
             @NonNull UserHandle userHandle,
             @NonNull RunServiceCallCallback<T> callback) {
         OneOffServiceConnection serviceConnection =
                 new OneOffServiceConnection(
-                        intent, bindFlags, timeoutInMillis, userHandle, callback);
+                        intent, bindFlags, userHandle, callback);
 
         return serviceConnection.bindAndRun();
     }
@@ -76,28 +75,17 @@
             implements ServiceConnection, ServiceUsageCompleteListener {
         private final Intent mIntent;
         private final int mFlags;
-        private final long mTimeoutMillis;
         private final UserHandle mUserHandle;
         private final RunServiceCallCallback<T> mCallback;
-        private final Runnable mTimeoutCallback;
 
         OneOffServiceConnection(
                 @NonNull Intent intent,
                 int flags,
-                long timeoutMillis,
                 @NonNull UserHandle userHandle,
                 @NonNull RunServiceCallCallback<T> callback) {
             mIntent = intent;
             mFlags = flags;
-            mTimeoutMillis = timeoutMillis;
             mCallback = callback;
-            mTimeoutCallback =
-                    () ->
-                            mExecutor.execute(
-                                    () -> {
-                                        safeUnbind();
-                                        mCallback.onTimedOut();
-                                    });
             mUserHandle = userHandle;
         }
 
@@ -105,9 +93,7 @@
             boolean bindServiceResult =
                     mContext.bindServiceAsUser(mIntent, this, mFlags, mUserHandle);
 
-            if (bindServiceResult) {
-                mHandler.postDelayed(mTimeoutCallback, mTimeoutMillis);
-            } else {
+            if(!bindServiceResult) {
                 safeUnbind();
             }
 
@@ -141,7 +127,6 @@
 
         private void safeUnbind() {
             try {
-                mHandler.removeCallbacks(mTimeoutCallback);
                 mContext.unbindService(this);
             } catch (Exception ex) {
                 Log.w(TAG, "Failed to unbind", ex);
diff --git a/services/appfunctions/lint-baseline.xml b/services/appfunctions/lint-baseline.xml
new file mode 100644
index 0000000..fbcb9f3
--- /dev/null
+++ b/services/appfunctions/lint-baseline.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha08" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha08">
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="executeAppFunction should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="    @Override"
+        errorLine2="    ^">
+        <location
+            file="frameworks/base/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java"
+            line="101"
+            column="5"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="onResult should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="                                                @Override"
+        errorLine2="                                                ^">
+        <location
+            file="frameworks/base/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java"
+            line="243"
+            column="49"/>
+    </issue>
+
+</issues>
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b53bf98..f6ac706 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -18,6 +18,7 @@
 
 import static android.appwidget.flags.Flags.remoteAdapterConversion;
 import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
+import static android.appwidget.flags.Flags.securityPolicyInteractAcrossUsers;
 import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -32,6 +33,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.PermissionName;
 import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -1442,7 +1444,7 @@
      * in {@link #allocateAppWidgetId}.
      *
      * @param callingPackage The package that calls this method.
-     * @param appWidgetId The id of theapp widget to bind.
+     * @param appWidgetId The id of the widget to bind.
      * @param providerProfileId The user/profile id of the provider.
      * @param providerComponent The {@link ComponentName} that provides the widget.
      * @param options The options to pass to the provider.
@@ -1463,13 +1465,15 @@
         mSecurityPolicy.enforceCallFromPackage(callingPackage);
 
         // Check that if a cross-profile binding is attempted, it is allowed.
-        if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)) {
+        // Cross-profile binding is also allowed if the caller has interact across users permission.
+        if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)
+                && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
             return false;
         }
 
-        // If the provider is not under the calling user, make sure this
-        // provider is allowlisted for access from the parent.
-        if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+        // If the provider is not under the calling user, make sure this provider is allowlisted for
+        // access from the parent, or that the caller has permission to interact across users.
+        if (!mSecurityPolicy.canAccessProvider(
                 providerComponent.getPackageName(), providerProfileId)) {
             return false;
         }
@@ -1734,6 +1738,14 @@
         return false;
     }
 
+    /**
+     * Called by a {@link AppWidgetHost} to remove all records (i.e. {@link Host}
+     * and all {@link Widget} associated with the host) from a specified host.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param hostId id of the {@link Host}.
+     * @see AppWidgetHost#deleteHost()
+     */
     @Override
     public void deleteHost(String callingPackage, int hostId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1767,6 +1779,15 @@
         }
     }
 
+    /**
+     * Called by a host process to remove all records (i.e. {@link Host}
+     * and all {@link Widget} associated with the host) from all hosts associated
+     * with the calling process.
+     *
+     * Typically used in clean up after test execution.
+     *
+     * @see AppWidgetHost#deleteAllHosts()
+     */
     @Override
     public void deleteAllHosts() {
         final int userId = UserHandle.getCallingUserId();
@@ -1801,6 +1822,18 @@
         }
     }
 
+    /**
+     * Returns the {@link AppWidgetProviderInfo} for the specified AppWidget.
+     *
+     * Typically used by launcher during the restore of an AppWidget, the binding
+     * of new AppWidget, and during grid size migration.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetId   Id of the widget.
+     * @return The {@link AppWidgetProviderInfo} for the specified widget.
+     *
+     * @see AppWidgetManager#getAppWidgetInfo(int)
+     */
     @Override
     public AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1855,6 +1888,17 @@
         }
     }
 
+    /**
+     * Returns the most recent {@link RemoteViews} of the specified AppWidget.
+     * Typically serves as a cache of the content of the AppWidget.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetId   Id of the widget.
+     * @return The {@link RemoteViews} of the specified widget.
+     *
+     * @see AppWidgetHost#updateAppWidgetDeferred(String, int)
+     * @see AppWidgetHost#setListener(int, AppWidgetHostListener)
+     */
     @Override
     public RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1882,6 +1926,29 @@
         }
     }
 
+    /**
+     * Update the extras for a given widget instance.
+     * <p>
+     * The extras can be used to embed additional information about this widget to be accessed
+     * by the associated widget's AppWidgetProvider.
+     *
+     * <p>
+     * The new options are merged into existing options using {@link Bundle#putAll} semantics.
+     *
+     * <p>
+     * Typically called by a {@link AppWidgetHost} (e.g. Launcher) to notify
+     * {@link AppWidgetProvider} regarding contextual changes (e.g. sizes) when rendering the
+     * widget.
+     * Calling this method would trigger onAppWidgetOptionsChanged() callback on the provider's
+     * side.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetId Id of the widget.
+     * @param options New options associate with this widget.
+     *
+     * @see AppWidgetManager#getAppWidgetOptions(int, Bundle)
+     * @see AppWidgetProvider#onAppWidgetOptionsChanged(Context, AppWidgetManager, int, Bundle)
+     */
     @Override
     public void updateAppWidgetOptions(String callingPackage, int appWidgetId, Bundle options) {
         final int userId = UserHandle.getCallingUserId();
@@ -1915,6 +1982,21 @@
         }
     }
 
+    /**
+     * Get the extras associated with a given widget instance.
+     * <p>
+     * The extras can be used to embed additional information about this widget to be accessed
+     * by the associated widget's AppWidgetProvider.
+     *
+     * Typically called by a host process (e.g. Launcher) to determine if they need to update the
+     * options of the widget.
+     *
+     * @see #updateAppWidgetOptions(String, int, Bundle)
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetId Id of the widget.
+     * @return The options associated with the specified widget instance.
+     */
     @Override
     public Bundle getAppWidgetOptions(String callingPackage, int appWidgetId) {
         final int userId = UserHandle.getCallingUserId();
@@ -1942,6 +2024,28 @@
         }
     }
 
+    /**
+     * Updates the content of the widgets (as specified by appWidgetIds) using the provided
+     * {@link RemoteViews}.
+     *
+     * Typically called by the provider's process. Either in response to the invocation of
+     * {@link AppWidgetProvider#onUpdate} or upon receiving the
+     * {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcast.
+     *
+     * <p>
+     * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
+     * contain a complete representation of the widget. For performing partial widget updates, see
+     * {@link #partiallyUpdateAppWidgetIds(String, int[], RemoteViews)}.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetIds Ids of the widgets to be updated.
+     * @param views The RemoteViews object containing the update.
+     *
+     * @see AppWidgetProvider#onUpdate(Context, AppWidgetManager, int[])
+     * @see AppWidgetManager#ACTION_APPWIDGET_UPDATE
+     * @see AppWidgetManager#updateAppWidget(int, RemoteViews)
+     * @see AppWidgetManager#updateAppWidget(int[], RemoteViews)
+     */
     @Override
     public void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
             RemoteViews views) {
@@ -1952,6 +2056,27 @@
         updateAppWidgetIds(callingPackage, appWidgetIds, views, false);
     }
 
+    /**
+     * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
+     * <p>
+     * This update  differs from {@link #updateAppWidgetIds(int[], RemoteViews)} in that the
+     * RemoteViews object which is passed is understood to be an incomplete representation of the
+     * widget, and hence does not replace the cached representation of the widget. As of API
+     * level 17, the new properties set within the views objects will be appended to the cached
+     * representation of the widget, and hence will persist.
+     *
+     * <p>
+     * This method will be ignored if a widget has not received a full update via
+     * {@link #updateAppWidget(int[], RemoteViews)}.
+     *
+     * @param callingPackage   The package that calls this method.
+     * @param appWidgetIds     Ids of the widgets to be updated.
+     * @param views            The RemoteViews object containing the incremental update / command.
+     *
+     * @see AppWidgetManager#partiallyUpdateAppWidget(int[], RemoteViews)
+     * @see RemoteViews#setDisplayedChild(int, int)
+     * @see RemoteViews#setScrollPosition(int, int)
+     */
     @Override
     public void partiallyUpdateAppWidgetIds(String callingPackage, int[] appWidgetIds,
             RemoteViews views) {
@@ -1962,6 +2087,24 @@
         updateAppWidgetIds(callingPackage, appWidgetIds, views, true);
     }
 
+    /**
+     * Callback function which marks specified providers as extended from AppWidgetProvider.
+     *
+     * This information is used to determine if the system can combine
+     * {@link AppWidgetManager#ACTION_APPWIDGET_ENABLED} and
+     * {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} into a single broadcast.
+     *
+     * Note: The system can only combine the two broadcasts if the provider is extended from
+     * AppWidgetProvider. When they do, they are expected to override the
+     * {@link AppWidgetProvider#onUpdate} callback function to provide updates, as opposed to
+     * listening for {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcasts directly.
+     *
+     * @see AppWidgetManager#ACTION_APPWIDGET_ENABLED
+     * @see AppWidgetManager#ACTION_APPWIDGET_UPDATE
+     * @see AppWidgetManager#ACTION_APPWIDGET_ENABLE_AND_UPDATE
+     * @see AppWidgetProvider#onReceive(Context, Intent)
+     * @see #sendEnableAndUpdateIntentLocked
+     */
     @Override
     public void notifyProviderInheritance(@Nullable final ComponentName[] componentNames) {
         final int userId = UserHandle.getCallingUserId();
@@ -1996,6 +2139,15 @@
         }
     }
 
+    /**
+     * Notifies the specified collection view in all the specified AppWidget instances
+     * to invalidate their data.
+     *
+     * This method is effectively deprecated since
+     * {@link RemoteViews#setRemoteAdapter(int, Intent)} has been deprecated.
+     *
+     * @see AppWidgetManager#notifyAppWidgetViewDataChanged(int[], int)
+     */
     @Override
     public void notifyAppWidgetViewDataChanged(String callingPackage, int[] appWidgetIds,
             int viewId) {
@@ -2031,6 +2183,18 @@
         }
     }
 
+    /**
+     * Updates the content of all widgets associated with given provider (as specified by
+     * componentName) using the provided {@link RemoteViews}.
+     *
+     * Typically called by the provider's process when there's an update that needs to be supplied
+     * to all instances of the widgets.
+     *
+     * @param componentName The component name of the provider.
+     * @param views The RemoteViews object containing the update.
+     *
+     * @see AppWidgetManager#updateAppWidget(ComponentName, RemoteViews)
+     */
     @Override
     public void updateAppWidgetProvider(ComponentName componentName, RemoteViews views) {
         final int userId = UserHandle.getCallingUserId();
@@ -2064,6 +2228,27 @@
         }
     }
 
+    /**
+     * Updates the info for the supplied AppWidget provider. Apps can use this to change the default
+     * behavior of the widget based on the state of the app (e.g., if the user is logged in
+     * or not). Calling this API completely replaces the previous definition.
+     *
+     * <p>
+     * The manifest entry of the provider should contain an additional meta-data tag similar to
+     * {@link AppWidgetManager#META_DATA_APPWIDGET_PROVIDER} which should point to any alternative
+     * definitions for the provider.
+     *
+     * <p>
+     * This is persisted across device reboots and app updates. If this meta-data key is not
+     * present in the manifest entry, the info reverts to default.
+     *
+     * @param provider {@link ComponentName} for the {@link
+     *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
+     * @param metaDataKey key for the meta-data tag pointing to the new provider info. Use null
+     *    to reset any previously set info.
+     *
+     * @see AppWidgetManager#updateAppWidgetProviderInfo(ComponentName, String)
+     */
     @Override
     public void updateAppWidgetProviderInfo(ComponentName componentName, String metadataKey) {
         final int userId = UserHandle.getCallingUserId();
@@ -2115,6 +2300,11 @@
         }
     }
 
+    /**
+     * Returns true if the default launcher app on the device (the one that currently
+     * holds the android.app.role.HOME role) can support pinning widgets
+     * (typically means adding widgets into home screen).
+     */
     @Override
     public boolean isRequestPinAppWidgetSupported() {
         synchronized (mLock) {
@@ -2129,6 +2319,44 @@
                         LauncherApps.PinItemRequest.REQUEST_TYPE_APPWIDGET);
     }
 
+    /**
+     * Request to pin an app widget on the current launcher. It's up to the launcher to accept this
+     * request (optionally showing a user confirmation). If the request is accepted, the caller will
+     * get a confirmation with extra {@link #EXTRA_APPWIDGET_ID}.
+     *
+     * <p>When a request is denied by the user, the caller app will not get any response.
+     *
+     * <p>Only apps with a foreground activity or a foreground service can call it.  Otherwise
+     * it'll throw {@link IllegalStateException}.
+     *
+     * <p>It's up to the launcher how to handle previous pending requests when the same package
+     * calls this API multiple times in a row.  It may ignore the previous requests,
+     * for example.
+     *
+     * <p>Launcher will not show the configuration activity associated with the provider in this
+     * case. The app could either show the configuration activity as a response to the callback,
+     * or show if before calling the API (various configurations can be encapsulated in
+     * {@code successCallback} to avoid persisting them before the widgetId is known).
+     *
+     * @param provider The {@link ComponentName} for the {@link
+     *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
+     * @param extras If not null, this is passed to the launcher app. For eg {@link
+     *    #EXTRA_APPWIDGET_PREVIEW} can be used for a custom preview.
+     * @param successCallback If not null, this intent will be sent when the widget is created.
+     *
+     * @return {@code TRUE} if the launcher supports this feature. Note the API will return without
+     *    waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean
+     *    the shortcut is pinned. {@code FALSE} if the launcher doesn't support this feature or if
+     *    calling app belongs to a user-profile with items restricted on home screen.
+     *
+     * @see android.content.pm.ShortcutManager#isRequestPinShortcutSupported()
+     * @see android.content.pm.ShortcutManager#requestPinShortcut(ShortcutInfo, IntentSender)
+     * @see AppWidgetManager#isRequestPinAppWidgetSupported()
+     * @see AppWidgetManager#requestPinAppWidget(ComponentName, Bundle, PendingIntent)
+     *
+     * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
+     * service or when the user is locked.
+     */
     @Override
     public boolean requestPinAppWidget(String callingPackage, ComponentName componentName,
             Bundle extras, IntentSender resultSender) {
@@ -2180,6 +2408,24 @@
                 callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
     }
 
+    /**
+     * Gets the AppWidget providers for the given user profile. User profile can only
+     * be the current user or a profile of the current user. For example, the current
+     * user may have a corporate profile. In this case the parent user profile has a
+     * child profile, the corporate one.
+     *
+     * @param categoryFilter Will only return providers which register as any of the specified
+     *        specified categories. See {@link AppWidgetProviderInfo#widgetCategory}.
+     * @param profile A profile of the current user which to be queried. The user
+     *        is itself also a profile. If null, the providers only for the current user
+     *        are returned.
+     * @param packageName If specified, will only return providers from the given package.
+     * @return The installed providers.
+     *
+     * @see android.os.Process#myUserHandle()
+     * @see android.os.UserManager#getUserProfiles()
+     * @see AppWidgetManager#getInstalledProvidersForProfile(int, UserHandle, String)
+     */
     @Override
     public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
             int profileId, String packageName) {
@@ -2190,8 +2436,10 @@
             Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
         }
 
-        // Ensure the profile is in the group and enabled.
-        if (!mSecurityPolicy.isEnabledGroupProfile(profileId)) {
+        // Ensure the profile is in the group and enabled, or that the caller has permission to
+        // interact across users.
+        if (!mSecurityPolicy.isEnabledGroupProfile(profileId)
+                && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
             return null;
         }
 
@@ -2226,7 +2474,7 @@
                 // Add providers only for the requested profile that are allowlisted.
                 final int providerProfileId = info.getProfile().getIdentifier();
                 if (providerProfileId == profileId
-                        && mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+                        && mSecurityPolicy.canAccessProvider(
                         providerPackageName, providerProfileId)
                         && !mPackageManagerInternal.filterAppAccess(providerPackageName, callingUid,
                         profileId)) {
@@ -2238,6 +2486,26 @@
         }
     }
 
+    /**
+     * Updates the content of the widgets (as specified by appWidgetIds) using the provided
+     * {@link RemoteViews}.
+     *
+     * If performing a partial update, the given RemoteViews object is merged into existing
+     * RemoteViews object.
+     *
+     * Fails silently if appWidgetIds is null or empty, or cannot found a widget with the given
+     * appWidgetId.
+     *
+     * @param callingPackage The package that calls this method.
+     * @param appWidgetIds Ids of the widgets to be updated.
+     * @param views The RemoteViews object containing the update.
+     * @param partially Whether it was a partial update.
+     *
+     * @see AppWidgetProvider#onUpdate(Context, AppWidgetManager, int[])
+     * @see AppWidgetManager#ACTION_APPWIDGET_UPDATE
+     * @see AppWidgetManager#updateAppWidget(int, RemoteViews)
+     * @see AppWidgetManager#updateAppWidget(int[], RemoteViews)
+     */
     private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
             RemoteViews views, boolean partially) {
         final int userId = UserHandle.getCallingUserId();
@@ -2267,12 +2535,29 @@
         }
     }
 
+    /**
+     * Increment the counter of widget ids and return the new id.
+     *
+     * Typically called by {@link #allocateAppWidgetId} when a instance of widget is created,
+     * either as a result of being pinned by launcher or added during a restore.
+     *
+     * Note: A widget id is a monotonically increasing integer that uniquely identifies the widget
+     * instance.
+     *
+     * TODO: Revisit this method and determine whether we need to alter the widget id during
+     *       the restore since widget id mismatch potentially leads to some issues in the past.
+     */
     private int incrementAndGetAppWidgetIdLocked(int userId) {
         final int appWidgetId = peekNextAppWidgetIdLocked(userId) + 1;
         mNextAppWidgetIds.put(userId, appWidgetId);
         return appWidgetId;
     }
 
+    /**
+     * Called by {@link #readProfileStateFromFileLocked} when widgets/providers/hosts are loaded
+     * from disk, which ensures mNextAppWidgetIds is larger than any existing widget id for given
+     * user.
+     */
     private void setMinAppWidgetIdLocked(int userId, int minWidgetId) {
         final int nextAppWidgetId = peekNextAppWidgetIdLocked(userId);
         if (nextAppWidgetId < minWidgetId) {
@@ -4620,7 +4905,7 @@
                 final int callingUid = Binder.getCallingUid();
                 final String providerPackageName = componentName.getPackageName();
                 final boolean providerIsInCallerProfile =
-                        mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+                        mSecurityPolicy.canAccessProvider(
                                 providerPackageName, providerProfileId);
                 final boolean shouldFilterAppAccess = mPackageManagerInternal.filterAppAccess(
                         providerPackageName, callingUid, providerProfileId);
@@ -4948,8 +5233,7 @@
             final int userId = UserHandle.getUserId(uid);
             if ((widget.host.getUserId() == userId || (widget.provider != null
                     && widget.provider.getUserId() == userId))
-                && mContext.checkCallingPermission(android.Manifest.permission.BIND_APPWIDGET)
-                    == PackageManager.PERMISSION_GRANTED) {
+                    && callerHasPermission(android.Manifest.permission.BIND_APPWIDGET)) {
                 // Apps that run in the same user as either the host or the provider and
                 // have the bind widget permission have access to the widget.
                 return true;
@@ -4968,12 +5252,20 @@
             return getProfileParent(profileId) == parentId;
         }
 
-        public boolean isProviderInCallerOrInProfileAndWhitelListed(String packageName,
-                int profileId) {
+        /**
+         * The provider is accessible by the caller if any of the following is true:
+         * - The provider belongs to the caller
+         * - The provider belongs to a profile of the caller and is allowlisted
+         * - The caller has permission to interact across users
+         */
+        public boolean canAccessProvider(String packageName, int profileId) {
             final int callerId = UserHandle.getCallingUserId();
             if (profileId == callerId) {
                 return true;
             }
+            if (hasCallerInteractAcrossUsersPermission()) {
+                return true;
+            }
             final int parentId = getProfileParent(profileId);
             if (parentId != callerId) {
                 return false;
@@ -5041,6 +5333,20 @@
             }
             return true;
         }
+
+        /** Returns true if the caller has permission to interact across users. */
+        public boolean hasCallerInteractAcrossUsersPermission() {
+            if (!securityPolicyInteractAcrossUsers()) {
+                return false;
+            }
+
+            return callerHasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+                    || callerHasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        }
+
+        private boolean callerHasPermission(@NonNull @PermissionName String permission) {
+            return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
+        }
     }
 
     static final class Provider {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index 46d60f9..0c54720 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -454,10 +454,10 @@
             @NonNull Associations associations)
             throws IOException {
         final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATIONS);
+        writeIntAttribute(serializer, XML_ATTR_MAX_ID, associations.getMaxId());
         for (AssociationInfo association : associations.getAssociations()) {
             writeAssociation(serializer, association);
         }
-        writeIntAttribute(serializer, XML_ATTR_MAX_ID, associations.getMaxId());
         serializer.endTag(null, XML_TAG_ASSOCIATIONS);
     }
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4e36e3f..c6e599e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -225,6 +225,7 @@
         "updates_flags_lib",
         "com_android_server_accessibility_flags_lib",
         "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
+        "com_android_launcher3_flags_lib",
         "com_android_wm_shell_flags_lib",
         "com.android.server.utils_aconfig-java",
         "service-jobscheduler-deviceidle.flags-aconfig-java",
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 0713999..60b826b 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -41,6 +41,7 @@
     public static final int CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER = 3;
     public static final int CPU_WAKEUP_SUBSYSTEM_SENSOR = 4;
     public static final int CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA = 5;
+    public static final int CPU_WAKEUP_SUBSYSTEM_BLUETOOTH = 6;
 
     /** @hide */
     @IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
@@ -50,6 +51,7 @@
             CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
             CPU_WAKEUP_SUBSYSTEM_SENSOR,
             CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA,
+            CPU_WAKEUP_SUBSYSTEM_BLUETOOTH,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CpuWakeupSubsystem {
@@ -99,6 +101,14 @@
     public abstract void noteCpuWakingNetworkPacket(Network network, long elapsedMillis, int uid);
 
     /**
+     * Informs battery stats of a sysproxy packet that woke up the CPU
+     *
+     * @param uid The uid that received the packet.
+     * @param elapsedMillis The time of the packet's arrival in elapsed timebase.
+     */
+    public abstract void noteCpuWakingBluetoothProxyPacket(int uid, long elapsedMillis);
+
+    /**
      * Informs battery stats of binder stats for the given work source UID.
      */
     public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index fd512a6..5b271a3 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -96,6 +96,8 @@
 27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3)
 # when a notification cancellation is prevented by the system
 27536 notification_cancel_prevented (key|3)
+# when a summary notification is converted to a regular notification because of force autogrouping
+27537 notification_summary_converted (key|3)
 
 # ---------------------------
 # Watchdog.java
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index cadceb5..a6e21fc 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -659,7 +659,7 @@
             @Override
             public void run() {
                 try {
-                    RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
+                    RecoverySystem.rebootPromptAndWipeUserData(context, TAG + "," + failedPackage);
                 } catch (Throwable t) {
                     logRescueException(level, failedPackage, t);
                 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d86bae1..e64a480 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -219,7 +219,7 @@
     public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
 
     /** Extended timeout for the system server watchdog. */
-    private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 20 * 1000;
+    private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 30 * 1000;
 
     /** Extended timeout for the system server watchdog for vold#partition operation. */
     private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
@@ -3251,7 +3251,7 @@
         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
             throw new SecurityException("no permission to commit checkpoint changes");
         }
-
+        extendWatchdogTimeout("vold#commitChanges might be slow");
         mVold.commitChanges();
     }
 
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index d19899f..12e8c57 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -178,9 +178,10 @@
 
     public static final boolean VDBG = false; // STOPSHIP: if true
 
+    // The system path is copied from Environment.getDataSystemDirectory
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     static final String VCN_CONFIG_FILE =
-            new File(Environment.getDataSystemDirectory(), "vcn/configs.xml").getPath();
+            new File(Environment.getDataDirectory(), "system/vcn/configs.xml").getPath();
 
     // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -379,10 +380,12 @@
         }
 
         /** Gets transports that need to be marked as restricted by the VCN from CarrierConfig */
+        // TODO: b/262269892 This method was created to perform experiments before the relevant API
+        // was exposed. Now it is obsolete and should be removed.
         @VisibleForTesting(visibility = Visibility.PRIVATE)
         public Set<Integer> getRestrictedTransportsFromCarrierConfig(
                 ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot) {
-            if (!Build.IS_ENG && !Build.IS_USERDEBUG) {
+            if (!Build.isDebuggable()) {
                 return RESTRICTED_TRANSPORTS_DEFAULT;
             }
 
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index b271d7e..ab69cd1 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -118,8 +118,11 @@
             if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) {
                 switchValues |= SW_LINEOUT_INSERT_BIT;
             }
-            notifyWiredAccessoryChanged(0, switchValues,
-                    SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT);
+            notifyWiredAccessoryChanged(
+                    0,
+                    switchValues,
+                    SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT,
+                    true /*isSynchronous*/);
         }
 
 
@@ -135,7 +138,13 @@
     }
 
     @Override
-    public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask) {
+    public void notifyWiredAccessoryChanged(
+            long whenNanos, int switchValues, int switchMask) {
+        notifyWiredAccessoryChanged(whenNanos, switchValues, switchMask, false /*isSynchronous*/);
+    }
+
+    public void notifyWiredAccessoryChanged(
+            long whenNanos, int switchValues, int switchMask, boolean isSynchronous) {
         if (LOG) {
             Slog.v(TAG, "notifyWiredAccessoryChanged: when=" + whenNanos
                     + " bits=" + switchCodeToString(switchValues, switchMask)
@@ -172,8 +181,10 @@
                     break;
             }
 
-            updateLocked(NAME_H2W,
-                    (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
+            updateLocked(
+                    NAME_H2W,
+                    (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset,
+                    isSynchronous);
         }
     }
 
@@ -195,8 +206,9 @@
      *
      * @param newName  One of the NAME_xxx variables defined above.
      * @param newState 0 or one of the BIT_xxx variables defined above.
+     * @param isSynchronous boolean to determine whether should happen sync or async
      */
-    private void updateLocked(String newName, int newState) {
+    private void updateLocked(String newName, int newState, boolean isSynchronous) {
         // Retain only relevant bits
         int headsetState = newState & SUPPORTED_HEADSETS;
         int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
@@ -234,12 +246,15 @@
             return;
         }
 
-        mWakeLock.acquire();
-
-        Log.i(TAG, "MSG_NEW_DEVICE_STATE");
-        Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
-                mHeadsetState, "");
-        mHandler.sendMessage(msg);
+        if (isSynchronous) {
+            setDevicesState(headsetState, mHeadsetState, "");
+        } else {
+            mWakeLock.acquire();
+            Log.i(TAG, "MSG_NEW_DEVICE_STATE");
+            Message msg = mHandler.obtainMessage(MSG_NEW_DEVICE_STATE, headsetState,
+                    mHeadsetState, "");
+            mHandler.sendMessage(msg);
+        }
 
         mHeadsetState = headsetState;
     }
@@ -439,7 +454,10 @@
             for (int i = 0; i < mUEventInfo.size(); ++i) {
                 UEventInfo uei = mUEventInfo.get(i);
                 if (devPath.equals(uei.getDevPath())) {
-                    updateLocked(name, uei.computeNewHeadsetState(mHeadsetState, state));
+                    updateLocked(
+                            name,
+                            uei.computeNewHeadsetState(mHeadsetState, state),
+                            false /*isSynchronous*/);
                     return;
                 }
             }
@@ -550,7 +568,10 @@
             synchronized (mLock) {
                 int mask = maskAndState.first;
                 int state = maskAndState.second;
-                updateLocked(name, mHeadsetState & ~(mask & ~state) | (mask & state));
+                updateLocked(
+                        name,
+                        mHeadsetState & ~(mask & ~state) | (mask & state),
+                        false /*isSynchronous*/);
                 return;
             }
         }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 765afef..3499a3a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1773,8 +1773,7 @@
                         // Create a Session for the target user and pass in the bundle
                         completeCloningAccount(response, result, account, toAccounts, userFrom);
                     } else {
-                        // Bundle format is not defined.
-                        super.onResultSkipSanitization(result);
+                        super.onResult(result);
                     }
                 }
             }.bind();
@@ -1861,8 +1860,7 @@
                     // account to avoid retries?
                     // TODO: what we do with the visibility?
 
-                    // Bundle format is not defined.
-                    super.onResultSkipSanitization(result);
+                    super.onResult(result);
                 }
 
                 @Override
@@ -2108,7 +2106,6 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
-            result = sanitizeBundle(result);
             IAccountManagerResponse response = getResponseAndClose();
             if (response != null) {
                 try {
@@ -2169,6 +2166,9 @@
             Log.i(TAG, "callingUid=" + callingUid + ", userId=" + accounts.userId
                     + " performing rename account");
             Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
+            if (resultingAccount == null) {
+                resultingAccount = accountToRename;
+            }
             Bundle result = new Bundle();
             result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
             result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
@@ -2459,7 +2459,6 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
-            result = sanitizeBundle(result);
             if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
                     && !result.containsKey(AccountManager.KEY_INTENT)) {
                 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
@@ -2974,7 +2973,6 @@
                 @Override
                 public void onResult(Bundle result) {
                     Bundle.setDefusable(result, true);
-                    result = sanitizeBundle(result);
                     if (result != null) {
                         String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
                         Bundle bundle = new Bundle();
@@ -3152,7 +3150,6 @@
                 @Override
                 public void onResult(Bundle result) {
                     Bundle.setDefusable(result, true);
-                    result = sanitizeBundle(result);
                     if (result != null) {
                         if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
                             Intent intent = newGrantCredentialsPermissionIntent(
@@ -3624,12 +3621,6 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
-            Bundle sessionBundle = null;
-            if (result != null) {
-                // Session bundle will be removed from result.
-                sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
-            }
-            result = sanitizeBundle(result);
             mNumResults++;
             Intent intent = null;
             if (result != null) {
@@ -3691,6 +3682,7 @@
             // bundle contains data necessary for finishing the session
             // later. The session bundle will be encrypted here and
             // decrypted later when trying to finish the session.
+            Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
             if (sessionBundle != null) {
                 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
                 if (TextUtils.isEmpty(accountType)
@@ -4078,7 +4070,6 @@
                 @Override
                 public void onResult(Bundle result) {
                     Bundle.setDefusable(result, true);
-                    result = sanitizeBundle(result);
                     IAccountManagerResponse response = getResponseAndClose();
                     if (response == null) {
                         return;
@@ -4392,7 +4383,6 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
-            result = sanitizeBundle(result);
             mNumResults++;
             if (result == null) {
                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
@@ -4949,68 +4939,6 @@
                 callback, resultReceiver);
     }
 
-
-    // All keys for Strings passed from AbstractAccountAuthenticator using Bundle.
-    private static final String[] sStringBundleKeys = new String[] {
-        AccountManager.KEY_ACCOUNT_NAME,
-        AccountManager.KEY_ACCOUNT_TYPE,
-        AccountManager.KEY_AUTHTOKEN,
-        AccountManager.KEY_AUTH_TOKEN_LABEL,
-        AccountManager.KEY_ERROR_MESSAGE,
-        AccountManager.KEY_PASSWORD,
-        AccountManager.KEY_ACCOUNT_STATUS_TOKEN};
-
-    /**
-     * Keep only documented fields in a Bundle received from AbstractAccountAuthenticator.
-     */
-    protected static Bundle sanitizeBundle(Bundle bundle) {
-        if (bundle == null) {
-            return null;
-        }
-        Bundle sanitizedBundle = new Bundle();
-        Bundle.setDefusable(sanitizedBundle, true);
-        int updatedKeysCount = 0;
-        for (String stringKey : sStringBundleKeys) {
-            if (bundle.containsKey(stringKey)) {
-                String value = bundle.getString(stringKey);
-                sanitizedBundle.putString(stringKey, value);
-                updatedKeysCount++;
-            }
-        }
-        String key = AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY;
-        if (bundle.containsKey(key)) {
-            long expiryMillis = bundle.getLong(key, 0L);
-            sanitizedBundle.putLong(key, expiryMillis);
-            updatedKeysCount++;
-        }
-        key = AccountManager.KEY_BOOLEAN_RESULT;
-        if (bundle.containsKey(key)) {
-            boolean booleanResult = bundle.getBoolean(key, false);
-            sanitizedBundle.putBoolean(key, booleanResult);
-            updatedKeysCount++;
-        }
-        key = AccountManager.KEY_ERROR_CODE;
-        if (bundle.containsKey(key)) {
-            int errorCode = bundle.getInt(key, 0);
-            sanitizedBundle.putInt(key, errorCode);
-            updatedKeysCount++;
-        }
-        key = AccountManager.KEY_INTENT;
-        if (bundle.containsKey(key)) {
-            Intent intent = bundle.getParcelable(key, Intent.class);
-            sanitizedBundle.putParcelable(key, intent);
-            updatedKeysCount++;
-        }
-        if (bundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)) {
-            // The field is not copied in sanitized bundle.
-            updatedKeysCount++;
-        }
-        if (updatedKeysCount != bundle.size()) {
-            Log.w(TAG, "Size mismatch after sanitizeBundle call.");
-        }
-        return sanitizedBundle;
-    }
-
     private abstract class Session extends IAccountAuthenticatorResponse.Stub
             implements IBinder.DeathRecipient, ServiceConnection {
         private final Object mSessionLock = new Object();
@@ -5301,15 +5229,10 @@
                 }
             }
         }
+
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
-            result = sanitizeBundle(result);
-            onResultSkipSanitization(result);
-        }
-
-        public void onResultSkipSanitization(Bundle result) {
-            Bundle.setDefusable(result, true);
             mNumResults++;
             Intent intent = null;
             if (result != null) {
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 34c3d7e..a73a991 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -74,6 +74,7 @@
 import android.util.Xml;
 
 import com.android.internal.R;
+import com.android.internal.annotations.Keep;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.util.FrameworkStatsLog;
@@ -214,7 +215,7 @@
 
     class PairingThread extends Thread implements NsdManager.RegistrationListener {
         private NsdManager mNsdManager;
-        private String mPublicKey;
+        @Keep private String mPublicKey;
         private String mPairingCode;
         private String mGuid;
         private String mServiceName;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 871c320..35323d6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -262,6 +262,7 @@
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
+import android.content.ClipData;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
@@ -397,6 +398,7 @@
 import com.android.internal.content.InstallLocationUtils;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.ApplicationSharedMemory;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
 import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer;
@@ -418,7 +420,6 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
-import com.android.server.crashrecovery.CrashRecoveryHelper;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.BootReceiver;
 import com.android.server.DeviceIdleInternal;
@@ -438,6 +439,7 @@
 import com.android.server.appop.AppOpsService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
+import com.android.server.crashrecovery.CrashRecoveryHelper;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.graphics.fonts.FontManagerInternal;
@@ -482,6 +484,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
@@ -499,6 +502,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -575,7 +579,7 @@
     static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100;
 
     // How many seconds should the system wait before terminating the spawned logcat process.
-    static final int LOGCAT_TIMEOUT_SEC = 10;
+    static final int LOGCAT_TIMEOUT_SEC = Flags.logcatLongerTimeout() ? 15 : 10;
 
     // Necessary ApplicationInfo flags to mark an app as persistent
     static final int PERSISTENT_MASK =
@@ -836,6 +840,8 @@
     @GuardedBy("this")
     final ComponentAliasResolver mComponentAliasResolver;
 
+    final FileDescriptor mApplicationSharedMemoryReadOnlyFd;
+
     private static final long HOME_LAUNCH_TIMEOUT_MS = 15000;
     private final AtomicBoolean mHasHomeDelay = new AtomicBoolean(false);
 
@@ -1038,13 +1044,14 @@
         @Override
         public void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
             synchronized (this) {
-                mProcessList.getAppStartInfoTracker().onIntentStarted(intent, timestampNanos);
+                mProcessList.getAppStartInfoTracker()
+                        .onActivityIntentStarted(intent, timestampNanos);
             }
         }
 
         @Override
         public void onIntentFailed(long id) {
-            mProcessList.getAppStartInfoTracker().onIntentFailed(id);
+            mProcessList.getAppStartInfoTracker().onActivityIntentFailed(id);
         }
 
         @Override
@@ -1078,7 +1085,7 @@
 
         @Override
         public void onReportFullyDrawn(long id, long timestampNanos) {
-            mProcessList.getAppStartInfoTracker().onReportFullyDrawn(id, timestampNanos);
+            mProcessList.getAppStartInfoTracker().onActivityReportFullyDrawn(id, timestampNanos);
         }
     };
 
@@ -2412,6 +2419,7 @@
         mBroadcastQueue = injector.getBroadcastQueue(this);
         mBroadcastController = new BroadcastController(mContext, this, mBroadcastQueue);
         mComponentAliasResolver = new ComponentAliasResolver(this);
+        mApplicationSharedMemoryReadOnlyFd = null;
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2518,6 +2526,13 @@
         mPendingStartActivityUids = new PendingStartActivityUids();
         mTraceErrorLogger = new TraceErrorLogger();
         mComponentAliasResolver = new ComponentAliasResolver(this);
+        try {
+            mApplicationSharedMemoryReadOnlyFd =
+                    ApplicationSharedMemory.getInstance().getReadOnlyFileDescriptor();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to get read only fd for shared memory", e);
+            throw new RuntimeException(e);
+        }
     }
 
     void setBroadcastQueueForTest(BroadcastQueue broadcastQueue) {
@@ -4724,6 +4739,7 @@
                         app.getDisabledCompatChanges(),
                         app.getLoggableCompatChanges(),
                         serializedSystemFontMap,
+                        mApplicationSharedMemoryReadOnlyFd,
                         app.getStartElapsedTime(),
                         app.getStartUptime());
             }
@@ -15304,10 +15320,8 @@
             }
 
             psr.setReportedForegroundServiceTypes(fgServiceTypes);
-            ProcessChangeItem item = mProcessList.enqueueProcessChangeItemLocked(
-                    proc.getPid(), proc.info.uid);
-            item.changes |= ProcessChangeItem.CHANGE_FOREGROUND_SERVICES;
-            item.foregroundServiceTypes = fgServiceTypes;
+            mProcessList.enqueueProcessChangeItemLocked(proc.getPid(), proc.info.uid,
+                    ProcessChangeItem.CHANGE_FOREGROUND_SERVICES, fgServiceTypes);
         }
         if (oomAdj) {
             updateOomAdjLocked(proc, OOM_ADJ_REASON_UI_VISIBILITY);
@@ -19086,4 +19100,87 @@
     Freezer getFreezer() {
         return mFreezer;
     }
+
+    // Set of IntentCreatorToken objects that are currently active.
+    private static final Map<IntentCreatorToken.Key, WeakReference<IntentCreatorToken>>
+            sIntentCreatorTokenCache = new ConcurrentHashMap<>();
+
+    /**
+     * A binder token used to keep track of which app created the intent. This token can be used to
+     * defend against intent redirect attacks. It stores uid of the intent creator and key fields of
+     * the intent to make it impossible for attacker to fake uid with a malicious intent.
+     *
+     * @hide
+     */
+    public static final class IntentCreatorToken extends Binder {
+        @NonNull
+        private final Key mKeyFields;
+
+        public IntentCreatorToken(int creatorUid, Intent intent) {
+            super();
+            this.mKeyFields = new Key(creatorUid, intent);
+        }
+
+        public int getCreatorUid() {
+            return mKeyFields.mCreatorUid;
+        }
+
+        /** {@hide} */
+        public static boolean isValid(@NonNull Intent intent) {
+            IBinder binder = intent.getCreatorToken();
+            IntentCreatorToken token = null;
+            if (binder instanceof IntentCreatorToken) {
+                token = (IntentCreatorToken) binder;
+            }
+            return token != null && token.mKeyFields.equals(
+                    new Key(token.mKeyFields.mCreatorUid, intent));
+        }
+
+        private static class Key {
+            private Key(int creatorUid, Intent intent) {
+                this.mCreatorUid = creatorUid;
+                this.mAction = intent.getAction();
+                this.mData = intent.getData();
+                this.mType = intent.getType();
+                this.mPackage = intent.getPackage();
+                this.mComponent = intent.getComponent();
+                this.mFlags = intent.getFlags() & Intent.IMMUTABLE_FLAGS;
+                ClipData clipData = intent.getClipData();
+                if (clipData != null) {
+                    this.mClipDataUris = new ArrayList<>(clipData.getItemCount());
+                    for (int i = 0; i < clipData.getItemCount(); i++) {
+                        this.mClipDataUris.add(clipData.getItemAt(i).getUri());
+                    }
+                }
+            }
+
+            private final int mCreatorUid;
+            private final String mAction;
+            private final Uri mData;
+            private final String mType;
+            private final String mPackage;
+            private final ComponentName mComponent;
+            private final int mFlags;
+            private List<Uri> mClipDataUris;
+
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+                Key key = (Key) o;
+                return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags && Objects.equals(
+                        mAction, key.mAction) && Objects.equals(mData, key.mData)
+                        && Objects.equals(mType, key.mType) && Objects.equals(mPackage,
+                        key.mPackage) && Objects.equals(mComponent, key.mComponent)
+                        && Objects.equals(mClipDataUris, key.mClipDataUris);
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent,
+                        mFlags,
+                        mClipDataUris);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0e19347..210301e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -133,6 +133,7 @@
 import com.android.server.am.nano.VMInfo;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.utils.AnrTimer;
 import com.android.server.utils.Slogf;
 
 import dalvik.annotation.optimization.NeverCompile;
@@ -285,6 +286,8 @@
                     return -1;
                 case "trace-ipc":
                     return runTraceIpc(pw);
+                case "trace-timer":
+                    return runTraceTimer(pw);
                 case "profile":
                     return runProfile(pw);
                 case "dumpheap":
@@ -1062,6 +1065,23 @@
         return 0;
     }
 
+    // Update AnrTimer tracing.
+    private int runTraceTimer(PrintWriter pw) throws RemoteException {
+        if (!AnrTimer.traceFeatureEnabled()) return -1;
+
+        // Delegate all argument parsing to the AnrTimer method.
+        try {
+            final String result = AnrTimer.traceTimers(peekRemainingArgs());
+            if (result != null) {
+                pw.println(result);
+            }
+            return 0;
+        } catch (IllegalArgumentException e) {
+            getErrPrintWriter().println("Error: bad trace-timer command: " + e);
+            return -1;
+        }
+    }
+
     // NOTE: current profiles can only be started on default display (even on automotive builds with
     // passenger displays), so there's no need to pass a display-id
     private int runProfile(PrintWriter pw) throws RemoteException {
@@ -4352,6 +4372,7 @@
             pw.println("      start: start tracing IPC transactions.");
             pw.println("      stop: stop tracing IPC transactions and dump the results to file.");
             pw.println("      --dump-file <FILE>: Specify the file the trace should be dumped to.");
+            anrTimerHelp(pw);
             pw.println("  profile start [--user <USER_ID> current]");
             pw.println("          [--clock-type <TYPE>]");
             pw.println("          [" + PROFILER_OUTPUT_VERSION_FLAG + " VERSION]");
@@ -4605,4 +4626,19 @@
             Intent.printIntentArgsHelp(pw, "");
         }
     }
+
+    static void anrTimerHelp(PrintWriter pw) {
+        // Return silently if tracing is not feature-enabled.
+        if (!AnrTimer.traceFeatureEnabled()) return;
+
+        String h = AnrTimer.traceTimers(new String[]{"help"});
+        if (h == null) {
+            return;
+        }
+
+        pw.println("  trace-timer <cmd>");
+        for (String s : h.split("\n")) {
+            pw.println("         " + s);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 6aadcdc..71b6456 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -280,7 +280,11 @@
         mTemporaryInProgressIndexes.clear();
     }
 
-    void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
+    /**
+     * Should only be called for Activity launch sequences from an instance of
+     * {@link ActivityMetricsLaunchObserver}.
+     */
+    void onActivityIntentStarted(@NonNull Intent intent, long timestampNanos) {
         synchronized (mLock) {
             if (!mEnabled) {
                 return;
@@ -291,6 +295,10 @@
             start.setStartType(ApplicationStartInfo.START_TYPE_UNSET);
             start.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_LAUNCH, timestampNanos);
 
+            if (android.app.Flags.appStartInfoComponent()) {
+                start.setStartComponent(ApplicationStartInfo.START_COMPONENT_ACTIVITY);
+            }
+
             // TODO: handle possible alarm activity start.
             if (intent != null && intent.getCategories() != null
                     && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
@@ -303,7 +311,11 @@
         }
     }
 
-    void onIntentFailed(long id) {
+    /**
+     * Should only be called for Activity launch sequences from an instance of
+     * {@link ActivityMetricsLaunchObserver}.
+     */
+    void onActivityIntentFailed(long id) {
         synchronized (mLock) {
             if (!mEnabled) {
                 return;
@@ -322,6 +334,10 @@
         }
     }
 
+    /**
+     * Should only be called for Activity launch sequences from an instance of
+     * {@link ActivityMetricsLaunchObserver}.
+     */
     void onActivityLaunched(long id, ComponentName name, long temperature, ProcessRecord app) {
         synchronized (mLock) {
             if (!mEnabled) {
@@ -349,6 +365,10 @@
         }
     }
 
+    /**
+     * Should only be called for Activity launch sequences from an instance of
+     * {@link ActivityMetricsLaunchObserver}.
+     */
     void onActivityLaunchCancelled(long id) {
         synchronized (mLock) {
             if (!mEnabled) {
@@ -368,6 +388,10 @@
         }
     }
 
+    /**
+     * Should only be called for Activity launch sequences from an instance of
+     * {@link ActivityMetricsLaunchObserver}.
+     */
     void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos,
             int launchMode) {
         synchronized (mLock) {
@@ -391,7 +415,11 @@
         }
     }
 
-    void onReportFullyDrawn(long id, long timestampNanos) {
+    /**
+     * Should only be called for Activity launch sequences from an instance of
+     * {@link ActivityMetricsLaunchObserver}.
+     */
+    void onActivityReportFullyDrawn(long id, long timestampNanos) {
         synchronized (mLock) {
             if (!mEnabled) {
                 return;
@@ -424,6 +452,10 @@
                     ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
             start.setStartType(ApplicationStartInfo.START_TYPE_COLD);
 
+            if (android.app.Flags.appStartInfoComponent()) {
+                start.setStartComponent(ApplicationStartInfo.START_COMPONENT_SERVICE);
+            }
+
             // TODO: handle possible alarm service start.
             start.setReason(serviceRecord.permission != null
                     && serviceRecord.permission.contains("android.permission.BIND_JOB_SERVICE")
@@ -455,6 +487,11 @@
                 start.setReason(ApplicationStartInfo.START_REASON_BROADCAST);
             }
             start.setIntent(intent);
+
+            if (android.app.Flags.appStartInfoComponent()) {
+                start.setStartComponent(ApplicationStartInfo.START_COMPONENT_BROADCAST);
+            }
+
             addStartInfoLocked(start);
         }
     }
@@ -472,6 +509,11 @@
                     ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs);
             start.setStartType(ApplicationStartInfo.START_TYPE_COLD);
             start.setReason(ApplicationStartInfo.START_REASON_CONTENT_PROVIDER);
+
+            if (android.app.Flags.appStartInfoComponent()) {
+                start.setStartComponent(ApplicationStartInfo.START_COMPONENT_CONTENT_PROVIDER);
+            }
+
             addStartInfoLocked(start);
         }
     }
@@ -490,6 +532,11 @@
             start.setStartType(cold ? ApplicationStartInfo.START_TYPE_COLD
                     : ApplicationStartInfo.START_TYPE_WARM);
             start.setReason(ApplicationStartInfo.START_REASON_BACKUP);
+
+            if (android.app.Flags.appStartInfoComponent()) {
+                start.setStartComponent(ApplicationStartInfo.START_COMPONENT_OTHER);
+            }
+
             addStartInfoLocked(start);
         }
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 75e9fad..ef82c74 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -437,7 +437,6 @@
         mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
                 mPowerAttributor, mPowerProfile, mCpuScalingPolicies,
                 mPowerStatsStore, Clock.SYSTEM_CLOCK);
-        mStats.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider, mPowerStatsStore);
         mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider);
         mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler);
         mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
@@ -504,6 +503,9 @@
     }
 
     public void systemServicesReady() {
+        mStats.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider, mPowerStatsStore,
+                Flags.accumulateBatteryUsageStats());
+
         MultiStatePowerAttributor attributor = (MultiStatePowerAttributor) mPowerAttributor;
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CPU,
                 Flags.streamlinedBatteryStats());
@@ -662,6 +664,8 @@
             } else if (nc.hasTransport(TRANSPORT_CELLULAR)) {
                 return CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
             }
+            // For TRANSPORT_BLUETOOTH, we have a separate channel to catch Bluetooth wakeups.
+            // See noteCpuWakingSysproxyPacket method.
             return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
         }
 
@@ -684,6 +688,15 @@
         }
 
         @Override
+        public void noteCpuWakingBluetoothProxyPacket(int uid, long elapsedMillis) {
+            if (uid < 0) {
+                Slog.e(TAG, "Invalid uid for waking bluetooth proxy packet: " + uid);
+                return;
+            }
+            noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH, elapsedMillis, uid);
+        }
+
+        @Override
         public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
                 Collection<BinderCallsStats.CallStat> callStats) {
             synchronized (BatteryStatsService.this.mLock) {
@@ -1100,14 +1113,17 @@
                                     DEVICE_CONFIG_NAMESPACE,
                                     MIN_CONSUMED_POWER_THRESHOLD_KEY,
                                     0);
-                    final BatteryUsageStatsQuery query =
-                            new BatteryUsageStatsQuery.Builder()
-                                    .setMaxStatsAgeMs(0)
-                                    .includeProcessStateData()
-                                    .includeVirtualUids()
-                                    .setMinConsumedPowerThreshold(minConsumedPowerThreshold)
-                                    .build();
-                    bus = getBatteryUsageStats(List.of(query)).get(0);
+                    BatteryUsageStatsQuery.Builder query = new BatteryUsageStatsQuery.Builder()
+                            .setMaxStatsAgeMs(0)
+                            .includeProcessStateData()
+                            .includeVirtualUids()
+                            .setMinConsumedPowerThreshold(minConsumedPowerThreshold);
+
+                    if (Flags.accumulateBatteryUsageStats()) {
+                        query.accumulated();
+                    }
+
+                    bus = getBatteryUsageStats(List.of(query.build())).get(0);
                     final int pullResult =
                             new StatsPerUidLogger(new FrameworkStatsLogger()).logStats(bus, data);
                     try {
@@ -3016,6 +3032,9 @@
         if (Flags.streamlinedBatteryStats()) {
             pw.println("  --sample: collect and dump a sample of stats for debugging purpose");
         }
+        if (Flags.accumulateBatteryUsageStats()) {
+            pw.println("  --accumulated: continuously accumulated since setup or reset-all");
+        }
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -3083,7 +3102,7 @@
     }
 
     private void dumpUsageStats(FileDescriptor fd, PrintWriter pw, int model,
-            boolean proto) {
+            boolean proto, boolean accumulated) {
         awaitCompletion();
         syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL);
 
@@ -3097,6 +3116,9 @@
         if (model == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
             builder.powerProfileModeledOnly();
         }
+        if (accumulated) {
+            builder.accumulated();
+        }
         BatteryUsageStatsQuery query = builder.build();
         synchronized (mStats) {
             mStats.prepareForDumpLocked();
@@ -3287,6 +3309,7 @@
                 } else if ("--usage".equals(arg)) {
                     int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
                     boolean proto = false;
+                    boolean accumulated = false;
                     for (int j = i + 1; j < args.length; j++) {
                         switch (args[j]) {
                             case "--proto":
@@ -3309,9 +3332,12 @@
                                 }
                                 break;
                             }
+                            case "--accumulated":
+                                accumulated = true;
+                                break;
                         }
                     }
-                    dumpUsageStats(fd, pw, model, proto);
+                    dumpUsageStats(fd, pw, model, proto, accumulated);
                     return;
                 } else if ("--wakeups".equals(arg)) {
                     mCpuWakeupStats.dump(new IndentingPrintWriter(pw, "  "),
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index f7085b4..15f1085 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -57,6 +57,7 @@
 import android.app.ApplicationThreadConstants;
 import android.app.BackgroundStartPrivileges;
 import android.app.BroadcastOptions;
+import android.app.BroadcastStickyCache;
 import android.app.IApplicationThread;
 import android.app.compat.CompatChanges;
 import android.appwidget.AppWidgetManager;
@@ -685,6 +686,7 @@
             boolean serialized, boolean sticky, int userId) {
         mService.enforceNotIsolatedCaller("broadcastIntent");
 
+        int result;
         synchronized (mService) {
             intent = verifyBroadcastLocked(intent);
 
@@ -706,7 +708,7 @@
 
             final long origId = Binder.clearCallingIdentity();
             try {
-                return broadcastIntentLocked(callerApp,
+                result = broadcastIntentLocked(callerApp,
                         callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
                         intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
                         resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
@@ -717,6 +719,10 @@
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
+        if (sticky && result == ActivityManager.BROADCAST_SUCCESS) {
+            BroadcastStickyCache.incrementVersion(intent.getAction());
+        }
+        return result;
     }
 
     // Not the binder call surface
@@ -727,6 +733,7 @@
             boolean serialized, boolean sticky, int userId,
             BackgroundStartPrivileges backgroundStartPrivileges,
             @Nullable int[] broadcastAllowList) {
+        int result;
         synchronized (mService) {
             intent = verifyBroadcastLocked(intent);
 
@@ -734,7 +741,7 @@
             String[] requiredPermissions = requiredPermission == null ? null
                     : new String[] {requiredPermission};
             try {
-                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
+                result = broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
                         resultToApp, resultTo, resultCode, resultData, resultExtras,
                         requiredPermissions, null, null, OP_NONE, bOptions, serialized, sticky, -1,
                         uid, realCallingUid, realCallingPid, userId,
@@ -744,6 +751,10 @@
                 Binder.restoreCallingIdentity(origId);
             }
         }
+        if (sticky && result == ActivityManager.BROADCAST_SUCCESS) {
+            BroadcastStickyCache.incrementVersion(intent.getAction());
+        }
+        return result;
     }
 
     @GuardedBy("mService")
@@ -1442,6 +1453,7 @@
                     list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive,
                             callingUid, callerAppProcessState, resolvedType));
                 }
+                BroadcastStickyCache.incrementVersion(intent.getAction());
             }
         }
 
@@ -1708,6 +1720,7 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
+        final ArrayList<String> changedStickyBroadcasts = new ArrayList<>();
         synchronized (mStickyBroadcasts) {
             ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
             if (stickies != null) {
@@ -1724,12 +1737,16 @@
                     if (list.size() <= 0) {
                         stickies.remove(intent.getAction());
                     }
+                    changedStickyBroadcasts.add(intent.getAction());
                 }
                 if (stickies.size() <= 0) {
                     mStickyBroadcasts.remove(userId);
                 }
             }
         }
+        for (int i = changedStickyBroadcasts.size() - 1; i >= 0; --i) {
+            BroadcastStickyCache.incrementVersionIfExists(changedStickyBroadcasts.get(i));
+        }
     }
 
     void finishReceiver(IBinder caller, int resultCode, String resultData,
@@ -1892,7 +1909,9 @@
 
     private void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
         mService.mProcessList.sendPackageBroadcastLocked(cmd, packages, userId);
-    }private List<ResolveInfo> collectReceiverComponents(
+    }
+
+    private List<ResolveInfo> collectReceiverComponents(
             Intent intent, String resolvedType, int callingUid, int callingPid,
             int[] users, int[] broadcastAllowList) {
         // TODO: come back and remove this assumption to triage all broadcasts
@@ -2108,9 +2127,18 @@
     }
 
     void removeStickyBroadcasts(int userId) {
+        final ArrayList<String> changedStickyBroadcasts = new ArrayList<>();
         synchronized (mStickyBroadcasts) {
+            final ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
+                    mStickyBroadcasts.get(userId);
+            if (stickies != null) {
+                changedStickyBroadcasts.addAll(stickies.keySet());
+            }
             mStickyBroadcasts.remove(userId);
         }
+        for (int i = changedStickyBroadcasts.size() - 1; i >= 0; --i) {
+            BroadcastStickyCache.incrementVersionIfExists(changedStickyBroadcasts.get(i));
+        }
     }
 
     @NeverCompile
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index d6f04db..2a30ad0 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -1,43 +1,46 @@
 # Applications & Processes
-yamasani@google.com
-jsharkey@google.com
-hackbod@google.com
-omakoto@google.com
-ctate@google.com
-huiyu@google.com
-mwachens@google.com
-sudheersai@google.com
-suprabh@google.com
-varunshah@google.com
-bookatz@google.com
-jji@google.com
-
-# Windows & Activities
-ogunwale@google.com
+per-file ActivityManager* = file:/ACTIVITY_MANAGER_OWNERS
+per-file ActiveServices.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ProcessList.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ActivityThread.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ProcessRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file SystemServer.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ServiceRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file AppProfiler.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ProcessStateRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ProcessServiceRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ForegroundServiceTypeLoggerModule.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file AppRestrictionController.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ProcessErrorStateRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ProcessProfileRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file ConnectionRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file UidRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file IntentBindRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file AppFGSTracker.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file FgsTempAllowList.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file HostingRecord.java = file:/ACTIVITY_MANAGER_OWNERS
+per-file App*ExitInfo* = file:/ACTIVITY_MANAGER_OWNERS
+per-file appexitinfo.proto = file:/ACTIVITY_MANAGER_OWNERS
+per-file App*StartInfo* = file:/PERFORMANCE_OWNERS
+per-file appstartinfo.proto = file:/PERFORMANCE_OWNERS
 
 # Permissions & Packages
-patb@google.com
 per-file AccessCheckDelegateHelper.java = file:/core/java/android/permission/OWNERS
 
 # Battery Stats
-joeo@google.com
+per-file AppBatteryTracker.java = file:/BATTERY_STATS_OWNERS
 per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
 per-file BatteryExternalStats* = file:/BATTERY_STATS_OWNERS
 
-# Londoners
-michaelwr@google.com
-narayan@google.com
-
 # Voice Interaction
 per-file *Assist* = file:/core/java/android/service/voice/OWNERS
 per-file *Voice* = file:/core/java/android/service/voice/OWNERS
 
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
+# Content Provider
+per-file ContentProvider* = varunshah@google.com, yamasani@google.com
 
-per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
-
-per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
-
+# Cached App Freezer
+per-file ProcessCachedOptimizerRecord.java = file:/PERFORMANCE_OWNERS
 per-file CachedAppOptimizer.java = file:/PERFORMANCE_OWNERS
 per-file Freezer.java = file:/PERFORMANCE_OWNERS
 
@@ -46,3 +49,23 @@
 
 # Broadcasts
 per-file Broadcast* = file:/BROADCASTS_OWNERS
+
+# Permissions & Packages
+per-file *Permission* = patb@google.com
+per-file *Package* = patb@google.com
+
+# OOM Adjuster
+per-file *Oom* = file:/OOM_ADJUSTER_OWNERS
+
+# Miscellaneous
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
+per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
+
+# Londoners
+michaelwr@google.com #{LAST_RESORT_SUGGESTION}
+narayan@google.com #{LAST_RESORT_SUGGESTION}
+
+# Default
+yamasani@google.com
+hackbod@google.com #{LAST_RESORT_SUGGESTION}
+omakoto@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 22ec790..78a0a11 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -3617,14 +3617,12 @@
         if (changes != 0) {
             if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
                     "Changes in " + app + ": " + changes);
-            ActivityManagerService.ProcessChangeItem item =
-                    mProcessList.enqueueProcessChangeItemLocked(app.getPid(), app.info.uid);
-            item.changes |= changes;
-            item.foregroundActivities = state.hasRepForegroundActivities();
+            mProcessList.enqueueProcessChangeItemLocked(app.getPid(), app.info.uid,
+                    changes, state.hasRepForegroundActivities());
             if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
-                    "Item " + Integer.toHexString(System.identityHashCode(item))
-                            + " " + app.toShortString() + ": changes=" + item.changes
-                            + " foreground=" + item.foregroundActivities
+                    "Enqueued process change item for "
+                            + app.toShortString() + ": changes=" + changes
+                            + " foreground=" + state.hasRepForegroundActivities()
                             + " type=" + state.getAdjType() + " source=" + state.getAdjSource()
                             + " target=" + state.getAdjTarget());
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 00250b4..a93ae72 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -4998,53 +4998,70 @@
     }
 
     @GuardedBy("mService")
-    ProcessChangeItem enqueueProcessChangeItemLocked(int pid, int uid) {
+    void enqueueProcessChangeItemLocked(int pid, int uid, int changes, int foregroundServicetypes) {
         synchronized (mProcessChangeLock) {
-            int i = mPendingProcessChanges.size() - 1;
-            ActivityManagerService.ProcessChangeItem item = null;
-            while (i >= 0) {
-                item = mPendingProcessChanges.get(i);
-                if (item.pid == pid) {
-                    if (DEBUG_PROCESS_OBSERVERS) {
-                        Slog.i(TAG_PROCESS_OBSERVERS, "Re-using existing item: " + item);
-                    }
-                    break;
-                }
-                i--;
-            }
-
-            if (i < 0) {
-                // No existing item in pending changes; need a new one.
-                final int num = mAvailProcessChanges.size();
-                if (num > 0) {
-                    item = mAvailProcessChanges.remove(num - 1);
-                    if (DEBUG_PROCESS_OBSERVERS) {
-                        Slog.i(TAG_PROCESS_OBSERVERS, "Retrieving available item: " + item);
-                    }
-                } else {
-                    item = new ActivityManagerService.ProcessChangeItem();
-                    if (DEBUG_PROCESS_OBSERVERS) {
-                        Slog.i(TAG_PROCESS_OBSERVERS, "Allocating new item: " + item);
-                    }
-                }
-                item.changes = 0;
-                item.pid = pid;
-                item.uid = uid;
-                if (mPendingProcessChanges.size() == 0) {
-                    if (DEBUG_PROCESS_OBSERVERS) {
-                        Slog.i(TAG_PROCESS_OBSERVERS, "*** Enqueueing dispatch processes changed!");
-                    }
-                    mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
-                            .sendToTarget();
-                }
-                mPendingProcessChanges.add(item);
-            }
-
-            return item;
+            final ProcessChangeItem item = enqueueProcessChangeItemLocked(pid, uid);
+            item.changes |= changes;
+            item.foregroundServiceTypes = foregroundServicetypes;
         }
     }
 
     @GuardedBy("mService")
+    void enqueueProcessChangeItemLocked(int pid, int uid, int changes,
+            boolean hasForegroundActivities) {
+        synchronized (mProcessChangeLock) {
+            final ProcessChangeItem item = enqueueProcessChangeItemLocked(pid, uid);
+            item.changes |= changes;
+            item.foregroundActivities = hasForegroundActivities;
+        }
+    }
+
+    @GuardedBy({"mService", "mProcessChangeLock"})
+    private ProcessChangeItem enqueueProcessChangeItemLocked(int pid, int uid) {
+        int i = mPendingProcessChanges.size() - 1;
+        ActivityManagerService.ProcessChangeItem item = null;
+        while (i >= 0) {
+            item = mPendingProcessChanges.get(i);
+            if (item.pid == pid) {
+                if (DEBUG_PROCESS_OBSERVERS) {
+                    Slog.i(TAG_PROCESS_OBSERVERS, "Re-using existing item: " + item);
+                }
+                break;
+            }
+            i--;
+        }
+
+        if (i < 0) {
+            // No existing item in pending changes; need a new one.
+            final int num = mAvailProcessChanges.size();
+            if (num > 0) {
+                item = mAvailProcessChanges.remove(num - 1);
+                if (DEBUG_PROCESS_OBSERVERS) {
+                    Slog.i(TAG_PROCESS_OBSERVERS, "Retrieving available item: " + item);
+                }
+            } else {
+                item = new ActivityManagerService.ProcessChangeItem();
+                if (DEBUG_PROCESS_OBSERVERS) {
+                    Slog.i(TAG_PROCESS_OBSERVERS, "Allocating new item: " + item);
+                }
+            }
+            item.changes = 0;
+            item.pid = pid;
+            item.uid = uid;
+            if (mPendingProcessChanges.size() == 0) {
+                if (DEBUG_PROCESS_OBSERVERS) {
+                    Slog.i(TAG_PROCESS_OBSERVERS, "*** Enqueueing dispatch processes changed!");
+                }
+                mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
+                        .sendToTarget();
+            }
+            mPendingProcessChanges.add(item);
+        }
+
+        return item;
+    }
+
+    @GuardedBy("mService")
     void scheduleDispatchProcessDiedLocked(int pid, int uid) {
         synchronized (mProcessChangeLock) {
             for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 3334393..9b51b6a 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -194,4 +194,15 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    name: "logcat_longer_timeout"
+    namespace: "backstage_power"
+    description: "Wait longer during the logcat gathering operation"
+    bug: "292533246"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6ae6f3d..e0cf96f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -423,7 +423,7 @@
 
     /** Hands the definition of foreground and uid states */
     @GuardedBy("this")
-    public AppOpsUidStateTracker getUidStateTracker() {
+    private AppOpsUidStateTracker getUidStateTracker() {
         if (mUidStateTracker == null) {
             mUidStateTracker = new AppOpsUidStateTrackerImpl(
                     LocalServices.getService(ActivityManagerInternal.class),
@@ -619,7 +619,7 @@
             this.op = op;
             this.uid = uid;
             this.uidState = uidState;
-            this.packageName = packageName;
+            this.packageName = packageName.intern();
             // We keep an invariant that the persistent device will always have an entry in
             // mDeviceAttributedOps.
             mDeviceAttributedOps.put(PERSISTENT_DEVICE_ID_DEFAULT,
@@ -1031,7 +1031,10 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            String pkgName = intent.getData().getEncodedSchemeSpecificPart();
+            if (action == null) {
+                return;
+            }
+            String pkgName = intent.getData().getEncodedSchemeSpecificPart().intern();
             int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
 
             if (action.equals(ACTION_PACKAGE_ADDED)
@@ -1235,7 +1238,7 @@
         Ops ops = uidState.pkgOps.get(packageName);
         if (ops == null) {
             ops = new Ops(packageName, uidState);
-            uidState.pkgOps.put(packageName, ops);
+            uidState.pkgOps.put(packageName.intern(), ops);
         }
 
         SparseIntArray packageModes =
@@ -2895,21 +2898,28 @@
                         uidState.uid, getPersistentId(virtualDeviceId), code);
 
                 if (rawUidMode != AppOpsManager.opToDefaultMode(code)) {
-                    return raw ? rawUidMode : uidState.evalMode(code, rawUidMode);
+                    return raw ? rawUidMode :
+                        evaluateForegroundMode(/* uid= */ uid, /* op= */ code,
+                        /* rawUidMode= */ rawUidMode);
                 }
             }
 
             Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
             if (op == null) {
-                return AppOpsManager.opToDefaultMode(code);
+                return evaluateForegroundMode(
+                        /* uid= */ uid,
+                        /* op= */ code,
+                        /* rawUidMode= */ AppOpsManager.opToDefaultMode(code));
             }
-            return raw
-                    ? mAppOpsCheckingService.getPackageMode(
-                            op.packageName, op.op, UserHandle.getUserId(op.uid))
-                    : op.uidState.evalMode(
-                            op.op,
-                            mAppOpsCheckingService.getPackageMode(
-                                    op.packageName, op.op, UserHandle.getUserId(op.uid)));
+            var packageMode = mAppOpsCheckingService.getPackageMode(
+                    op.packageName,
+                    op.op,
+                    UserHandle.getUserId(op.uid));
+            return raw ? packageMode :
+                    evaluateForegroundMode(
+                        /* uid= */ uid,
+                        /* op= */op.op,
+                        /* rawUidMode= */ packageMode);
         }
     }
 
@@ -4739,7 +4749,7 @@
                 return null;
             }
             ops = new Ops(packageName, uidState);
-            uidState.pkgOps.put(packageName, ops);
+            uidState.pkgOps.put(packageName.intern(), ops);
         }
 
         if (edit) {
@@ -5076,7 +5086,7 @@
         Ops ops = uidState.pkgOps.get(pkgName);
         if (ops == null) {
             ops = new Ops(pkgName, uidState);
-            uidState.pkgOps.put(pkgName, ops);
+            uidState.pkgOps.put(pkgName.intern(), ops);
         }
         ops.put(op.op, op);
     }
@@ -7003,6 +7013,11 @@
                 "Requested persistentId for invalid virtualDeviceId: " + virtualDeviceId);
     }
 
+    @GuardedBy("this")
+    private int evaluateForegroundMode(int uid, int op, int rawUidMode) {
+        return getUidStateTracker().evalMode(uid, op, rawUidMode);
+    }
+
     private final class ClientUserRestrictionState implements DeathRecipient {
         private final IBinder token;
 
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 55d9c6e..87504154 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -17,6 +17,11 @@
 
 import static android.media.audio.Flags.scoManagedByAudio;
 
+import static com.android.media.audio.Flags.equalScoLeaVcIndexRange;
+import static com.android.server.audio.AudioService.BT_COMM_DEVICE_ACTIVE_BLE_HEADSET;
+import static com.android.server.audio.AudioService.BT_COMM_DEVICE_ACTIVE_BLE_SPEAKER;
+import static com.android.server.audio.AudioService.BT_COMM_DEVICE_ACTIVE_SCO;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
@@ -64,6 +69,7 @@
 import android.util.PrintWriterPrinter;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.audio.AudioService.BtCommDeviceActiveType;
 import com.android.server.utils.EventLogger;
 
 import java.io.PrintWriter;
@@ -333,8 +339,8 @@
             Log.v(TAG, "setCommunicationDevice, device: " + device + ", uid: " + uid);
         }
 
-        synchronized (mDeviceStateLock) {
-            if (device == null) {
+        if (device == null) {
+            synchronized (mDeviceStateLock) {
                 CommunicationRouteClient client = getCommunicationRouteClientForUid(uid);
                 if (client == null) {
                     return false;
@@ -835,15 +841,15 @@
         return isDeviceOnForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
     }
 
-    /*package*/ boolean isBluetoothScoActive() {
+    private boolean isBluetoothScoActive() {
         return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
     }
 
-    /*package*/ boolean isBluetoothBleHeadsetActive() {
+    private boolean isBluetoothBleHeadsetActive() {
         return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLE_HEADSET);
     }
 
-    /*package*/ boolean isBluetoothBleSpeakerActive() {
+    private boolean isBluetoothBleSpeakerActive() {
         return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLE_SPEAKER);
     }
 
@@ -1315,9 +1321,9 @@
         sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info);
     }
 
-    /*package*/ void postSetModeOwner(int mode, int pid, int uid) {
-        sendLMsgNoDelay(MSG_I_SET_MODE_OWNER, SENDMSG_REPLACE,
-                new AudioModeInfo(mode, pid, uid));
+    /*package*/ void postSetModeOwner(int mode, int pid, int uid, boolean signal) {
+        sendILMsgNoDelay(MSG_IL_SET_MODE_OWNER, SENDMSG_REPLACE,
+                signal ? 1 : 0, new AudioModeInfo(mode, pid, uid));
     }
 
     /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
@@ -1437,7 +1443,20 @@
         }
         mCurCommunicationPortId = portId;
 
-        mAudioService.postScoDeviceActive(isBluetoothScoActive());
+        @BtCommDeviceActiveType int btCommDeviceActiveType = 0;
+        if (equalScoLeaVcIndexRange()) {
+            if (isBluetoothScoActive()) {
+                btCommDeviceActiveType = BT_COMM_DEVICE_ACTIVE_SCO;
+            } else if (isBluetoothBleHeadsetActive()) {
+                btCommDeviceActiveType = BT_COMM_DEVICE_ACTIVE_BLE_HEADSET;
+            } else if (isBluetoothBleSpeakerActive()) {
+                btCommDeviceActiveType = BT_COMM_DEVICE_ACTIVE_BLE_SPEAKER;
+            }
+            mAudioService.postBtCommDeviceActive(btCommDeviceActiveType);
+        } else {
+            mAudioService.postBtCommDeviceActive(
+                    isBluetoothScoActive() ? BT_COMM_DEVICE_ACTIVE_SCO : btCommDeviceActiveType);
+        }
 
         final int nbDispatchers = mCommDevDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; i++) {
@@ -1545,38 +1564,6 @@
         sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
     }
 
-    /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
-                                                            List<AudioDeviceAttributes> devices)
-    {
-        sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy, devices);
-    }
-
-    /*package*/ void postSaveRemovePreferredDevicesForStrategy(int strategy) {
-        sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
-    }
-
-    /*package*/ void postSaveSetDeviceAsNonDefaultForStrategy(
-            int strategy, AudioDeviceAttributes device) {
-        sendILMsgNoDelay(MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
-    }
-
-    /*package*/ void postSaveRemoveDeviceAsNonDefaultForStrategy(
-            int strategy, AudioDeviceAttributes device) {
-        sendILMsgNoDelay(
-                MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
-    }
-
-    /*package*/ void postSaveSetPreferredDevicesForCapturePreset(
-            int capturePreset, List<AudioDeviceAttributes> devices) {
-        sendILMsgNoDelay(
-                MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset, devices);
-    }
-
-    /*package*/ void postSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
-        sendIMsgNoDelay(
-                MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
-    }
-
     /*package*/ void postUpdateCommunicationRouteClient(
             int btScoRequesterUid, String eventSource) {
         sendILMsgNoDelay(MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE,
@@ -2006,7 +1993,7 @@
                         mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
                     }
                     break;
-                case MSG_I_SET_MODE_OWNER:
+                case MSG_IL_SET_MODE_OWNER:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
                             int btScoRequesterUid = bluetoothScoRequestOwnerUid();
@@ -2017,6 +2004,9 @@
                             }
                         }
                     }
+                    if (msg.arg1 == 1 /*signal*/) {
+                        mAudioService.decrementAudioModeResetCount();
+                    }
                     break;
 
                 case MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT:
@@ -2090,40 +2080,9 @@
                         mDeviceInventory.setBluetoothActiveDevice(btInfo);
                     }
                 } break;
-                case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
-                    final int strategy = msg.arg1;
-                    final List<AudioDeviceAttributes> devices =
-                            (List<AudioDeviceAttributes>) msg.obj;
-                    mDeviceInventory.onSaveSetPreferredDevices(strategy, devices);
-                } break;
-                case MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
-                    final int strategy = msg.arg1;
-                    mDeviceInventory.onSaveRemovePreferredDevices(strategy);
-                } break;
-                case MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY: {
-                    final int strategy = msg.arg1;
-                    final AudioDeviceAttributes device = (AudioDeviceAttributes) msg.obj;
-                    mDeviceInventory.onSaveSetDeviceAsNonDefault(strategy, device);
-                } break;
-                case MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY: {
-                    final int strategy = msg.arg1;
-                    final AudioDeviceAttributes device = (AudioDeviceAttributes) msg.obj;
-                    mDeviceInventory.onSaveRemoveDeviceAsNonDefault(strategy, device);
-                } break;
                 case MSG_CHECK_MUTE_MUSIC:
                     checkMessagesMuteMusic(0);
                     break;
-                case MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET: {
-                    final int capturePreset = msg.arg1;
-                    final List<AudioDeviceAttributes> devices =
-                            (List<AudioDeviceAttributes>) msg.obj;
-                    mDeviceInventory.onSaveSetPreferredDevicesForCapturePreset(
-                            capturePreset, devices);
-                } break;
-                case MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET: {
-                    final int capturePreset = msg.arg1;
-                    mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
-                } break;
                 case MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED: {
                     final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
                     BtHelper.onNotifyPreferredAudioProfileApplied(btDevice);
@@ -2205,7 +2164,7 @@
     private static final int MSG_REPORT_NEW_ROUTES = 13;
     private static final int MSG_II_SET_HEARING_AID_VOLUME = 14;
     private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
-    private static final int MSG_I_SET_MODE_OWNER = 16;
+    private static final int MSG_IL_SET_MODE_OWNER = 16;
 
     private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22;
     private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23;
@@ -2216,16 +2175,10 @@
     // process external command to (dis)connect a hearing aid device
     private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
 
-    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32;
-    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33;
-
     private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34;
     private static final int MSG_CHECK_MUTE_MUSIC = 35;
     private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
 
-    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
-    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
-
     private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42;
     private static final int MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
 
@@ -2234,8 +2187,6 @@
     // process set volume for Le Audio, obj is BleVolumeInfo
     private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46;
 
-    private static final int MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY = 47;
-    private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
     private static final int MSG_IIL_BTLEAUDIO_TIMEOUT = 49;
 
     private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 52;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index a9bff8b..5fd12c2 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -548,14 +548,17 @@
     @GuardedBy("mDevicesLock")
     private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
 
+    @GuardedBy("mDevicesLock")
     // List of preferred devices for strategies
     private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
             new ArrayMap<>();
 
+    @GuardedBy("mDevicesLock")
     // List of non-default devices for strategies
     private final ArrayMap<Integer, List<AudioDeviceAttributes>> mNonDefaultDevices =
             new ArrayMap<>();
 
+    @GuardedBy("mDevicesLock")
     // List of preferred devices of capture preset
     private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset =
             new ArrayMap<>();
@@ -808,24 +811,18 @@
         synchronized (mDevicesLock) {
             mAppliedStrategyRoles.clear();
             mAppliedPresetRoles.clear();
-        }
-        synchronized (mPreferredDevices) {
             mPreferredDevices.forEach((strategy, devices) -> {
                 setPreferredDevicesForStrategy(strategy, devices);
             });
-        }
-        synchronized (mNonDefaultDevices) {
             mNonDefaultDevices.forEach((strategy, devices) -> {
                 addDevicesRoleForStrategy(strategy, AudioSystem.DEVICE_ROLE_DISABLED,
                         devices, false /* internal */);
             });
-        }
-        synchronized (mPreferredDevicesForCapturePreset) {
             mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
                 setDevicesRoleForCapturePreset(
                         capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
             });
-        }
+       }
     }
 
     /** only public for mocking/spying, do not call outside of AudioService */
@@ -1225,7 +1222,8 @@
         mmi.record();
     }
 
-    /*package*/ void onSaveSetPreferredDevices(int strategy,
+    @GuardedBy("mDevicesLock")
+    private void saveSetPreferredDevices(int strategy,
                                                @NonNull List<AudioDeviceAttributes> devices) {
         mPreferredDevices.put(strategy, devices);
         List<AudioDeviceAttributes> nonDefaultDevices = mNonDefaultDevices.get(strategy);
@@ -1243,12 +1241,14 @@
         dispatchPreferredDevice(strategy, devices);
     }
 
-    /*package*/ void onSaveRemovePreferredDevices(int strategy) {
+    @GuardedBy("mDevicesLock")
+    private void saveRemovePreferredDevices(int strategy) {
         mPreferredDevices.remove(strategy);
         dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
     }
 
-    /*package*/ void onSaveSetDeviceAsNonDefault(int strategy,
+    @GuardedBy("mDevicesLock")
+    private void saveSetDeviceAsNonDefault(int strategy,
                                                  @NonNull AudioDeviceAttributes device) {
         List<AudioDeviceAttributes> nonDefaultDevices = mNonDefaultDevices.get(strategy);
         if (nonDefaultDevices == null) {
@@ -1272,7 +1272,8 @@
         }
     }
 
-    /*package*/ void onSaveRemoveDeviceAsNonDefault(int strategy,
+    @GuardedBy("mDevicesLock")
+    private void saveRemoveDeviceAsNonDefault(int strategy,
                                                     @NonNull AudioDeviceAttributes device) {
         List<AudioDeviceAttributes> nonDefaultDevices = mNonDefaultDevices.get(strategy);
         if (nonDefaultDevices != null) {
@@ -1282,14 +1283,16 @@
         }
     }
 
-    /*package*/ void onSaveSetPreferredDevicesForCapturePreset(
+    @GuardedBy("mDevicesLock")
+    private void saveSetPreferredDevicesForCapturePreset(
             int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
         mPreferredDevicesForCapturePreset.put(capturePreset, devices);
         dispatchDevicesRoleForCapturePreset(
                 capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
     }
 
-    /*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+    @GuardedBy("mDevicesLock")
+    private void saveClearPreferredDevicesForCapturePreset(int capturePreset) {
         mPreferredDevicesForCapturePreset.remove(capturePreset);
         dispatchDevicesRoleForCapturePreset(
                 capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED,
@@ -1301,21 +1304,22 @@
 
     /*package*/ int setPreferredDevicesForStrategyAndSave(int strategy,
             @NonNull List<AudioDeviceAttributes> devices) {
-        final int status = setPreferredDevicesForStrategy(strategy, devices);
-        if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices);
+        synchronized(mDevicesLock){
+            try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+                final int status = setPreferredDevicesForStrategy(strategy, devices);
+                if (status == AudioSystem.SUCCESS) {
+                    saveSetPreferredDevices(strategy, devices);
+                }
+                return status;
+            }
         }
-        return status;
     }
     // Only used for external requests coming from an API
     /*package*/ int setPreferredDevicesForStrategy(int strategy,
             @NonNull List<AudioDeviceAttributes> devices) {
-        int status = AudioSystem.ERROR;
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            status = setDevicesRoleForStrategy(
-                    strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, false /* internal */);
-        }
-        return status;
+
+        return setDevicesRoleForStrategy(
+                strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, false /* internal */);
     }
     // Only used for internal requests
     /*package*/ int setPreferredDevicesForStrategyInt(int strategy,
@@ -1326,21 +1330,21 @@
     }
 
     /*package*/ int removePreferredDevicesForStrategyAndSave(int strategy) {
-        final int status = removePreferredDevicesForStrategy(strategy);
-        if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
+        synchronized(mDevicesLock){
+            try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+                final int status = removePreferredDevicesForStrategy(strategy);
+                if (status == AudioSystem.SUCCESS) {
+                    saveRemovePreferredDevices(strategy);
+                }
+                return status;
+            }
         }
-        return status;
     }
     // Only used for external requests coming from an API
     /*package*/ int removePreferredDevicesForStrategy(int strategy) {
-        int status = AudioSystem.ERROR;
 
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            status = clearDevicesRoleForStrategy(
+        return clearDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_PREFERRED, false /*internal */);
-        }
-        return status;
     }
     // Only used for internal requests
     /*package*/ int removePreferredDevicesForStrategyInt(int strategy) {
@@ -1351,16 +1355,17 @@
     /*package*/ int setDeviceAsNonDefaultForStrategyAndSave(int strategy,
             @NonNull AudioDeviceAttributes device) {
         int status = AudioSystem.ERROR;
-
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            List<AudioDeviceAttributes> devices = new ArrayList<>();
-            devices.add(device);
-            status = addDevicesRoleForStrategy(
+        synchronized(mDevicesLock){
+            try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+                List<AudioDeviceAttributes> devices = new ArrayList<>();
+                devices.add(device);
+                status = addDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */);
-        }
 
-        if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveSetDeviceAsNonDefaultForStrategy(strategy, device);
+                if (status == AudioSystem.SUCCESS) {
+                    saveSetDeviceAsNonDefault(strategy, device);
+                }
+            }
         }
         return status;
     }
@@ -1368,16 +1373,17 @@
     /*package*/ int removeDeviceAsNonDefaultForStrategyAndSave(int strategy,
             @NonNull AudioDeviceAttributes device) {
         int status = AudioSystem.ERROR;
-
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            List<AudioDeviceAttributes> devices = new ArrayList<>();
-            devices.add(device);
-            status = removeDevicesRoleForStrategy(
+        synchronized(mDevicesLock){
+            try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+                List<AudioDeviceAttributes> devices = new ArrayList<>();
+                devices.add(device);
+                status = removeDevicesRoleForStrategy(
                     strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */);
-        }
 
-        if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveRemoveDeviceAsNonDefaultForStrategy(strategy, device);
+                if (status == AudioSystem.SUCCESS) {
+                    saveRemoveDeviceAsNonDefault(strategy, device);
+                }
+            }
         }
         return status;
     }
@@ -1405,41 +1411,40 @@
 
     /*package*/ int setPreferredDevicesForCapturePresetAndSave(
             int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
-        final int status = setPreferredDevicesForCapturePreset(capturePreset, devices);
-        if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
+        synchronized(mDevicesLock){
+            try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+                final int status = setPreferredDevicesForCapturePreset(capturePreset, devices);
+                if (status == AudioSystem.SUCCESS) {
+                    saveSetPreferredDevicesForCapturePreset(capturePreset, devices);
+                }
+                return status;
+            }
         }
-        return status;
     }
 
     // Only used for external requests coming from an API
     private int setPreferredDevicesForCapturePreset(
             int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
-        int status = AudioSystem.ERROR;
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            status = setDevicesRoleForCapturePreset(
+        return setDevicesRoleForCapturePreset(
                     capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
-        }
-        return status;
     }
 
     /*package*/ int clearPreferredDevicesForCapturePresetAndSave(int capturePreset) {
-        final int status  = clearPreferredDevicesForCapturePreset(capturePreset);
-        if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
+        synchronized(mDevicesLock){
+            try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+                final int status  = clearPreferredDevicesForCapturePreset(capturePreset);
+                if (status == AudioSystem.SUCCESS) {
+                   saveClearPreferredDevicesForCapturePreset(capturePreset);
+                }
+                return status;
+            }
         }
-        return status;
     }
 
     // Only used for external requests coming from an API
     private int clearPreferredDevicesForCapturePreset(int capturePreset) {
-        int status  = AudioSystem.ERROR;
-
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
-            status = clearDevicesRoleForCapturePreset(
+        return clearDevicesRoleForCapturePreset(
                     capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
-        }
-        return status;
     }
 
     // Only used for internal requests
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 99404428..561030e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -66,6 +66,7 @@
 import static com.android.media.audio.Flags.asDeviceConnectionFailure;
 import static com.android.media.audio.Flags.audioserverPermissions;
 import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
+import static com.android.media.audio.Flags.equalScoLeaVcIndexRange;
 import static com.android.media.audio.Flags.replaceStreamBtSco;
 import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
 import static com.android.media.audio.Flags.setStreamVolumeOrder;
@@ -470,7 +471,7 @@
     private static final int MSG_CONFIGURATION_CHANGED = 54;
     private static final int MSG_BROADCAST_MASTER_MUTE = 55;
     private static final int MSG_UPDATE_CONTEXTUAL_VOLUMES = 56;
-    private static final int MSG_SCO_DEVICE_ACTIVE_UPDATE = 57;
+    private static final int MSG_BT_COMM_DEVICE_ACTIVE_UPDATE = 57;
 
     /**
      * Messages handled by the {@link SoundDoseHelper}, do not exceed
@@ -766,7 +767,21 @@
      * @see System#MUTE_STREAMS_AFFECTED */
     private int mUserMutableStreams;
 
-    private final AtomicBoolean mScoDeviceActive = new AtomicBoolean(false);
+    /** The active bluetooth device type used for communication is sco. */
+    /*package*/ static final int BT_COMM_DEVICE_ACTIVE_SCO = 1;
+    /** The active bluetooth device type used for communication is ble headset. */
+    /*package*/ static final int BT_COMM_DEVICE_ACTIVE_BLE_HEADSET = 1 << 1;
+    /** The active bluetooth device type used for communication is ble speaker. */
+    /*package*/ static final int BT_COMM_DEVICE_ACTIVE_BLE_SPEAKER = 1 << 2;
+    @IntDef({
+            BT_COMM_DEVICE_ACTIVE_SCO, BT_COMM_DEVICE_ACTIVE_BLE_HEADSET,
+            BT_COMM_DEVICE_ACTIVE_BLE_SPEAKER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BtCommDeviceActiveType {
+    }
+
+    private final AtomicInteger mBtCommDeviceActive = new AtomicInteger(0);
 
     @NonNull
     private SoundEffectsHelper mSfxHelper;
@@ -1900,7 +1915,7 @@
         // Restore call state
         synchronized (mDeviceBroker.mSetModeLock) {
             onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
-                    mContext.getPackageName(), true /*force*/);
+                    mContext.getPackageName(), true /*force*/, false /*signal*/);
         }
         final int forSys;
         synchronized (mSettingsLock) {
@@ -2522,12 +2537,18 @@
                 // this should not happen, throwing exception
                 throw new IllegalArgumentException("STREAM_BLUETOOTH_SCO is deprecated");
             }
-            return streamType == AudioSystem.STREAM_VOICE_CALL && mScoDeviceActive.get();
+            return streamType == AudioSystem.STREAM_VOICE_CALL
+                    && mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO;
         } else {
             return streamType == AudioSystem.STREAM_BLUETOOTH_SCO;
         }
     }
 
+    private boolean isStreamBluetoothComm(int streamType) {
+        return (streamType == AudioSystem.STREAM_VOICE_CALL && mBtCommDeviceActive.get() != 0)
+                || streamType == AudioSystem.STREAM_BLUETOOTH_SCO;
+    }
+
     private void dumpStreamStates(PrintWriter pw) {
         pw.println("\nStream volumes (device: index)");
         int numStreamTypes = AudioSystem.getNumStreamTypes();
@@ -4722,17 +4743,50 @@
                 }
             }
             if (updateAudioMode) {
-                sendMsg(mAudioHandler,
-                        MSG_UPDATE_AUDIO_MODE,
-                        existingMsgPolicy,
-                        AudioSystem.MODE_CURRENT,
-                        android.os.Process.myPid(),
-                        mContext.getPackageName(),
-                        delay);
+                postUpdateAudioMode(existingMsgPolicy, AudioSystem.MODE_CURRENT,
+                        android.os.Process.myPid(), mContext.getPackageName(),
+                        false /*signal*/, delay);
             }
         }
     }
 
+    static class UpdateAudioModeInfo {
+        UpdateAudioModeInfo(int mode, int pid, String packageName, boolean signal) {
+            mMode = mode;
+            mPid = pid;
+            mPackageName = packageName;
+            mSignal = signal;
+        }
+        private final int mMode;
+        private final int mPid;
+        private final String mPackageName;
+        private final boolean mSignal;
+
+        int getMode() {
+            return mMode;
+        }
+        int getPid() {
+            return mPid;
+        }
+        String getPackageName() {
+            return mPackageName;
+        }
+        boolean getSignal() {
+            return mSignal;
+        }
+    }
+
+    void postUpdateAudioMode(int msgPolicy, int mode, int pid, String packageName,
+            boolean signal, int delay) {
+        synchronized (mAudioModeResetLock) {
+            if (signal) {
+                mAudioModeResetCount++;
+            }
+            sendMsg(mAudioHandler, MSG_UPDATE_AUDIO_MODE, msgPolicy, 0, 0,
+                new UpdateAudioModeInfo(mode, pid, packageName, signal), delay);
+        }
+    }
+
     private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor =
             new IRecordingConfigDispatcher.Stub() {
         @Override
@@ -4761,7 +4815,7 @@
                 + asDeviceConnectionFailure());
         pw.println("\tandroid.media.audio.autoPublicVolumeApiHardening:"
                 + autoPublicVolumeApiHardening());
-        pw.println("\tandroid.media.audio.Flags.automaticBtDeviceType:"
+        pw.println("\tandroid.media.audio.automaticBtDeviceType:"
                 + automaticBtDeviceType());
         pw.println("\tandroid.media.audio.featureSpatialAudioHeadtrackingLowLatency:"
                 + featureSpatialAudioHeadtrackingLowLatency());
@@ -4783,6 +4837,8 @@
                 + absVolumeIndexFix());
         pw.println("\tcom.android.media.audio.replaceStreamBtSco:"
                 + replaceStreamBtSco());
+        pw.println("\tcom.android.media.audio.equalScoLeaVcIndexRange:"
+                + equalScoLeaVcIndexRange());
     }
 
     private void dumpAudioMode(PrintWriter pw) {
@@ -4896,7 +4952,7 @@
         final VolumeStreamState streamState = getVssForStreamOrDefault(streamTypeAlias);
 
         if (!replaceStreamBtSco() && (streamType == AudioManager.STREAM_VOICE_CALL)
-                && isInCommunication() && mDeviceBroker.isBluetoothScoActive()) {
+                && isInCommunication() && mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
             Log.i(TAG, "setStreamVolume for STREAM_VOICE_CALL, switching to STREAM_BLUETOOTH_SCO");
             streamType = AudioManager.STREAM_BLUETOOTH_SCO;
         }
@@ -5947,10 +6003,10 @@
         final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
                 || ringerMode == AudioManager.RINGER_MODE_SILENT;
         final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
-                && mDeviceBroker.isBluetoothScoActive();
+                && mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO;
         final boolean shouldRingBle = ringerMode == AudioManager.RINGER_MODE_VIBRATE
-                && (mDeviceBroker.isBluetoothBleHeadsetActive()
-                || mDeviceBroker.isBluetoothBleSpeakerActive());
+                && (mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_BLE_HEADSET
+                || mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_BLE_SPEAKER);
         // Ask audio policy engine to force use Bluetooth SCO/BLE channel if needed
         final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
                 + "/" + Binder.getCallingPid();
@@ -6129,13 +6185,9 @@
                 } else {
                     SetModeDeathHandler h = mSetModeDeathHandlers.get(index);
                     mSetModeDeathHandlers.remove(index);
-                    sendMsg(mAudioHandler,
-                            MSG_UPDATE_AUDIO_MODE,
-                            SENDMSG_QUEUE,
-                            AudioSystem.MODE_CURRENT,
-                            android.os.Process.myPid(),
-                            mContext.getPackageName(),
-                            0);
+                    postUpdateAudioMode(SENDMSG_QUEUE, AudioSystem.MODE_CURRENT,
+                            android.os.Process.myPid(), mContext.getPackageName(),
+                            false /*signal*/, 0);
                 }
             }
         }
@@ -6381,19 +6433,14 @@
                 }
             }
 
-            sendMsg(mAudioHandler,
-                    MSG_UPDATE_AUDIO_MODE,
-                    SENDMSG_REPLACE,
-                    mode,
-                    pid,
-                    callingPackage,
-                    0);
+            postUpdateAudioMode(SENDMSG_REPLACE, mode, pid, callingPackage,
+                    hasModifyPhoneStatePermission && mode == AudioSystem.MODE_NORMAL, 0);
         }
     }
 
     @GuardedBy("mDeviceBroker.mSetModeLock")
     void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage,
-                           boolean force) {
+                           boolean force, boolean signal) {
         if (requestedMode == AudioSystem.MODE_CURRENT) {
             requestedMode = getMode();
         }
@@ -6408,7 +6455,7 @@
         }
         if (DEBUG_MODE) {
             Log.v(TAG, "onUpdateAudioMode() new mode: " + mode + ", current mode: "
-                    + mMode.get() + " requested mode: " + requestedMode);
+                    + mMode.get() + " requested mode: " + requestedMode + " signal: " + signal);
         }
         if (mode != mMode.get() || force) {
             int status = AudioSystem.SUCCESS;
@@ -6454,7 +6501,7 @@
 
                 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
                 // connections not started by the application changing the mode when pid changes
-                mDeviceBroker.postSetModeOwner(mode, pid, uid);
+                mDeviceBroker.postSetModeOwner(mode, pid, uid, signal);
             } else {
                 Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
             }
@@ -7419,7 +7466,8 @@
         case AudioSystem.PLATFORM_VOICE:
             if (isInCommunication()
                     || mAudioSystem.isStreamActive(AudioManager.STREAM_VOICE_CALL, 0)) {
-                if (!replaceStreamBtSco() && mDeviceBroker.isBluetoothScoActive()) {
+                if (!replaceStreamBtSco()
+                        && mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
                     if (DEBUG_VOL) {
                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
                     }
@@ -7463,7 +7511,8 @@
             }
         default:
             if (isInCommunication()) {
-                if (!replaceStreamBtSco() && mDeviceBroker.isBluetoothScoActive()) {
+                if (!replaceStreamBtSco()
+                        && mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
                 } else {
@@ -7788,15 +7837,15 @@
                 0 /*delay*/);
     }
 
-    /*package*/ void postScoDeviceActive(boolean scoDeviceActive) {
+    /*package*/ void postBtCommDeviceActive(@BtCommDeviceActiveType int btCommDeviceActive) {
         sendMsg(mAudioHandler,
-                MSG_SCO_DEVICE_ACTIVE_UPDATE,
-                SENDMSG_QUEUE, scoDeviceActive ? 1 : 0 /*arg1*/, 0 /*arg2*/, null /*obj*/,
+                MSG_BT_COMM_DEVICE_ACTIVE_UPDATE,
+                SENDMSG_QUEUE, btCommDeviceActive /*arg1*/, 0 /*arg2*/, null /*obj*/,
                 0 /*delay*/);
     }
 
-    private void onUpdateScoDeviceActive(boolean scoDeviceActive) {
-        if (mScoDeviceActive.compareAndSet(!scoDeviceActive, scoDeviceActive)) {
+    private void onUpdateBtCommDeviceActive(@BtCommDeviceActiveType int btCommDeviceActive) {
+        if (mBtCommDeviceActive.getAndSet(btCommDeviceActive) != btCommDeviceActive) {
             getVssForStreamOrDefault(AudioSystem.STREAM_VOICE_CALL).updateIndexFactors();
         }
     }
@@ -8997,7 +9046,7 @@
         }
 
         public void updateIndexFactors() {
-            if (!replaceStreamBtSco()) {
+            if (!replaceStreamBtSco() && !equalScoLeaVcIndexRange()) {
                 return;
             }
 
@@ -9008,10 +9057,18 @@
                         mIndexMax = MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
                     }
 
-                    // SCO devices have a different min index
-                    if (isStreamBluetoothSco(mStreamType)) {
+                    if (!equalScoLeaVcIndexRange() && isStreamBluetoothSco(mStreamType)) {
+                        // SCO devices have a different min index
                         mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
                         mIndexStepFactor = 1.f;
+                    } else if (equalScoLeaVcIndexRange() && isStreamBluetoothComm(mStreamType)) {
+                        // For non SCO devices the stream state does not change the min index
+                        if (mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
+                            mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
+                        } else {
+                            mIndexMin = MIN_STREAM_VOLUME[mStreamType] * 10;
+                        }
+                        mIndexStepFactor = 1.f;
                     } else {
                         mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] * 10;
                         mIndexStepFactor = (float) (mIndexMax - mIndexMin) / (float) (
@@ -9207,7 +9264,7 @@
         private void setStreamVolumeIndex(int index, int device) {
             // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
             // This allows RX path muting by the audio HAL only when explicitly muted but not when
-            // index is just set to 0 to repect BT requirements
+            // index is just set to 0 to respect BT requirements
             if (isStreamBluetoothSco(mStreamType) && index == 0 && !isFullyMuted()) {
                 index = 1;
             }
@@ -10129,7 +10186,7 @@
                         h.setRecordingActive(isRecordingActiveForUid(h.getUid()));
                         if (wasActive != h.isActive()) {
                             onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
-                                    mContext.getPackageName(), false /*force*/);
+                                    mContext.getPackageName(), false /*force*/, false /*signal*/);
                         }
                     }
                     break;
@@ -10159,7 +10216,9 @@
 
                 case MSG_UPDATE_AUDIO_MODE:
                     synchronized (mDeviceBroker.mSetModeLock) {
-                        onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj, false /*force*/);
+                        UpdateAudioModeInfo info = (UpdateAudioModeInfo) msg.obj;
+                        onUpdateAudioMode(info.getMode(), info.getPid(), info.getPackageName(),
+                                false /*force*/, info.getSignal());
                     }
                     break;
 
@@ -10217,8 +10276,8 @@
                     onUpdateContextualVolumes();
                     break;
 
-                case MSG_SCO_DEVICE_ACTIVE_UPDATE:
-                    onUpdateScoDeviceActive(msg.arg1 != 0);
+                case MSG_BT_COMM_DEVICE_ACTIVE_UPDATE:
+                    onUpdateBtCommDeviceActive(msg.arg1);
                     break;
 
                 case MusicFxHelper.MSG_EFFECT_CLIENT_GONE:
@@ -10809,7 +10868,8 @@
             //TODO move inside HardeningEnforcer after refactor that moves permission checks
             //     in the blockFocusMethod
             if (permissionOverridesCheck) {
-                mHardeningEnforcer.metricsLogFocusReq(/*blocked*/false, focusReqType, uid);
+                mHardeningEnforcer.metricsLogFocusReq(/*blocked*/ false, focusReqType, uid,
+                        /*unblockedBySdk*/ false);
             }
             if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid,
                     HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS,
@@ -10861,9 +10921,59 @@
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
         mmi.record();
+        //delay abandon focus requests from Telecom if an audio mode reset from Telecom
+        // is still being processed
+        final boolean abandonFromTelecom = (mContext.checkCallingOrSelfPermission(
+                    MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)
+                && ((aa != null && aa.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION)
+                        || AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId));
+        if (abandonFromTelecom) {
+            synchronized (mAudioModeResetLock) {
+                final long start = java.lang.System.currentTimeMillis();
+                long elapsed = 0;
+                while (mAudioModeResetCount > 0) {
+                    if (DEBUG_MODE) {
+                        Log.i(TAG, "Abandon focus from Telecom, waiting for mode change");
+                    }
+                    try {
+                        mAudioModeResetLock.wait(
+                                AUDIO_MODE_RESET_TIMEOUT_MS - elapsed);
+                    } catch (InterruptedException e) {
+                        Log.w(TAG, "Interrupted while waiting for audio mode reset");
+                    }
+                    elapsed = java.lang.System.currentTimeMillis() - start;
+                    if (elapsed >= AUDIO_MODE_RESET_TIMEOUT_MS) {
+                        Log.e(TAG, "Timeout waiting for audio mode reset");
+                        break;
+                    }
+                }
+                if (DEBUG_MODE && elapsed != 0) {
+                    Log.i(TAG, "Abandon focus from Telecom done waiting");
+                }
+            }
+        }
         return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
     }
 
+    /** synchronization between setMode(NORMAL) and abandonAudioFocus() frmo Telecom */
+    private static final long AUDIO_MODE_RESET_TIMEOUT_MS = 3000;
+
+    private final Object mAudioModeResetLock = new Object();
+
+    @GuardedBy("mAudioModeResetLock")
+    private int mAudioModeResetCount = 0;
+
+    void decrementAudioModeResetCount() {
+        synchronized (mAudioModeResetLock) {
+            if (mAudioModeResetCount > 0) {
+                mAudioModeResetCount--;
+            } else {
+                Log.w(TAG, "mAudioModeResetCount already 0");
+            }
+            mAudioModeResetLock.notify();
+        }
+    }
+
     /** see {@link AudioManager#abandonAudioFocusForTest(AudioFocusRequest, String)} */
     public int abandonAudioFocusForTest(IAudioFocusDispatcher fd, String clientId,
             AudioAttributes aa, String callingPackageName) {
@@ -13383,19 +13493,39 @@
     }
 
     @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
-    /** @see AudioPolicy#getFocusStack() */
+    /* @see AudioPolicy#getFocusStack() */
     public List<AudioFocusInfo> getFocusStack() {
         super.getFocusStack_enforcePermission();
 
         return mMediaFocusControl.getFocusStack();
     }
 
-    /** @see AudioPolicy#sendFocusLoss */
+    /**
+     * @param focusLoser non-null entry that may be in the stack
+     * @see AudioPolicy#sendFocusLossAndUpdate(AudioFocusInfo)
+     */
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
+    public void sendFocusLossAndUpdate(@NonNull AudioFocusInfo focusLoser,
+            @NonNull IAudioPolicyCallback apcb) {
+        super.sendFocusLossAndUpdate_enforcePermission();
+        Objects.requireNonNull(apcb);
+        if (!mAudioPolicies.containsKey(apcb.asBinder())) {
+            throw new IllegalStateException("Only registered AudioPolicy can change focus");
+        }
+        if (!mAudioPolicies.get(apcb.asBinder()).mHasFocusListener) {
+            throw new IllegalStateException("AudioPolicy must have focus listener to change focus");
+        }
+
+        mMediaFocusControl.sendFocusLossAndUpdate(Objects.requireNonNull(focusLoser));
+    }
+
+    /* @see AudioPolicy#sendFocusLoss(AudioFocusInfo)  */
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser,
             @NonNull IAudioPolicyCallback apcb) {
+        super.sendFocusLoss_enforcePermission();
         Objects.requireNonNull(focusLoser);
         Objects.requireNonNull(apcb);
-        enforceModifyAudioRoutingPermission();
         if (!mAudioPolicies.containsKey(apcb.asBinder())) {
             throw new IllegalStateException("Only registered AudioPolicy can change focus");
         }
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index ce92dfb..b421264 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -605,7 +605,11 @@
                 break;
             case BluetoothProfile.LE_AUDIO:
                 if (mLeAudio != null && mLeAudioCallback != null) {
-                    mLeAudio.unregisterCallback(mLeAudioCallback);
+                    try {
+                        mLeAudio.unregisterCallback(mLeAudioCallback);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Exception while unregistering callback for LE audio", e);
+                    }
                 }
                 mLeAudio = null;
                 mLeAudioCallback = null;
@@ -682,12 +686,21 @@
                     return;
                 }
                 if (mLeAudio != null && mLeAudioCallback != null) {
-                    mLeAudio.unregisterCallback(mLeAudioCallback);
+                    try {
+                        mLeAudio.unregisterCallback(mLeAudioCallback);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Exception while unregistering callback for LE audio", e);
+                    }
                 }
                 mLeAudio = (BluetoothLeAudio) proxy;
                 mLeAudioCallback = new MyLeAudioCallback();
-                mLeAudio.registerCallback(
-                            mContext.getMainExecutor(), mLeAudioCallback);
+                try{
+                    mLeAudio.registerCallback(
+                                mContext.getMainExecutor(), mLeAudioCallback);
+                } catch (Exception e) {
+                    mLeAudioCallback = null;
+                    Log.e(TAG, "Exception while registering callback for LE audio", e);
+                }
                 break;
             case BluetoothProfile.A2DP_SINK:
             case BluetoothProfile.LE_AUDIO_BROADCAST:
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index faeba5d..6611110 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -168,6 +168,8 @@
         }
 
         boolean blocked = true;
+        // indicates the focus request was not blocked because of the SDK version
+        boolean unblockedBySdk = false;
         if (noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName, attributionTag)) {
             if (DEBUG) {
                 Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking");
@@ -179,9 +181,10 @@
                         + targetSdk);
             }
             blocked = false;
+            unblockedBySdk = true;
         }
 
-        metricsLogFocusReq(blocked, focusReqType, callingUid);
+        metricsLogFocusReq(blocked, focusReqType, callingUid, unblockedBySdk);
 
         if (!blocked) {
             return false;
@@ -195,7 +198,16 @@
         return true;
     }
 
-    /*package*/ void metricsLogFocusReq(boolean blocked, int focusReq, int callingUid) {
+    /**
+     * Log metrics for the focus request
+     * @param blocked true if the call blocked
+     * @param focusReq the type of focus request
+     * @param callingUid the UID of the caller
+     * @param unblockedBySdk if blocked is false,
+     *                       true indicates it was unblocked thanks to an older SDK
+     */
+    /*package*/ void metricsLogFocusReq(boolean blocked, int focusReq, int callingUid,
+            boolean unblockedBySdk) {
         final String metricId = blocked ? METRIC_COUNTERS_FOCUS_DENIAL.get(focusReq)
                 : METRIC_COUNTERS_FOCUS_GRANT.get(focusReq);
         if (TextUtils.isEmpty(metricId)) {
@@ -204,6 +216,12 @@
         }
         try {
             Counter.logIncrementWithUid(metricId, callingUid);
+            if (!blocked && unblockedBySdk) {
+                // additional metric to capture focus requests that are currently granted
+                // because the app is on an older SDK, but would have been blocked otherwise
+                Counter.logIncrementWithUid(
+                        "media_audio.value_audio_focus_grant_hardening_waived_by_sdk", callingUid);
+            }
         } catch (Exception e) {
             Slog.e(TAG, "Counter error metricId:" + metricId + " for focus req:" + focusReq
                     + " from uid:" + callingUid, e);
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 7e26356..b4af46e 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -280,6 +280,37 @@
     }
 
     /**
+     * Like {@link #sendFocusLoss(AudioFocusInfo)} but if the loser was at the top of stack,
+     * make the next entry gain focus with {@link AudioManager#AUDIOFOCUS_GAIN}.
+     * @param focusInfo the focus owner to discard
+     * @see AudioPolicy#sendFocusLossAndUpdate(AudioFocusInfo)
+     */
+    protected void sendFocusLossAndUpdate(@NonNull AudioFocusInfo focusInfo) {
+        synchronized (mAudioFocusLock) {
+            if (mFocusStack.isEmpty()) {
+                return;
+            }
+            final FocusRequester currentFocusOwner = mFocusStack.peek();
+            if (currentFocusOwner.toAudioFocusInfo().equals(focusInfo)) {
+                // focus loss is for the top of the stack
+                currentFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
+                            false /*forceDuck*/);
+                currentFocusOwner.release();
+
+                mFocusStack.pop();
+                // is there a new focus owner?
+                if (!mFocusStack.isEmpty()) {
+                    mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
+                }
+            } else {
+                // focus loss if for another entry that's not at the top of the stack,
+                // just remove it from the stack and make it lose focus
+                sendFocusLoss(focusInfo);
+            }
+        }
+    }
+
+    /**
      * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo
      * instead of FocusRequester instances)
      * @return a SystemApi-friendly version of the focus stack, in the same order (last entry
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 5d85089..2d802b2 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -49,7 +49,6 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.biometrics.SensorPropertiesInternal;
-import android.hardware.biometrics.face.IFace;
 import android.hardware.face.FaceSensorConfigurations;
 import android.hardware.face.FaceSensorProperties;
 import android.hardware.face.FaceSensorPropertiesInternal;
@@ -73,6 +72,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemService;
+import com.android.server.biometrics.sensors.face.FaceService;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintService;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 
@@ -211,7 +211,7 @@
          */
         @VisibleForTesting
         public String[] getFaceAidlInstances() {
-            return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+            return FaceService.getDeclaredInstances();
         }
 
         /**
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index bd6d593..8c98872 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_FACE;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.hardware.face.FaceSensorConfigurations.getIFace;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -60,6 +61,7 @@
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.SystemService;
@@ -753,7 +755,7 @@
     public FaceService(Context context) {
         this(context, null /* faceProviderFunction */, () -> IBiometricService.Stub.asInterface(
                 ServiceManager.getService(Context.BIOMETRIC_SERVICE)), null /* faceProvider */,
-                () -> ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR));
+                () -> getDeclaredInstances());
     }
 
     @VisibleForTesting FaceService(Context context,
@@ -778,8 +780,7 @@
 
         mFaceProvider = faceProvider != null ? faceProvider : (name) -> {
             final String fqName = IFace.DESCRIPTOR + "/" + name;
-            final IFace face = IFace.Stub.asInterface(
-                    Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
+            final IFace face = getIFace(fqName);
             if (face == null) {
                 Slog.e(TAG, "Unable to get declared service: " + fqName);
                 return null;
@@ -835,6 +836,23 @@
      */
     public static native void releaseSurfaceHandle(@NonNull NativeHandle handle);
 
+    /**
+     * Get all face hal instances declared in manifest
+     * @return instance names
+     */
+    public static String[] getDeclaredInstances() {
+        String[] a = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
+        Slog.i(TAG, "Before:getDeclaredInstances: IFace instance found, a.length="
+                + a.length);
+        if (!ArrayUtils.contains(a, "virtual")) {
+            // Now, the virtual hal is registered with IVirtualHal interface and it is also
+            //   moved from vendor to system_ext partition without a device manifest. So
+            //   if the old vhal is not declared, add here.
+            a = ArrayUtils.appendElement(String.class, a, "virtual");
+        }
+        Slog.i(TAG, "After:getDeclaredInstances: a.length=" + a.length);
+        return a;
+    }
 
     void syncEnrollmentsNow() {
         Utils.checkPermissionOrShell(getContext(), MANAGE_FACE);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index dca1491..3ed01d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -23,6 +23,9 @@
 import android.hardware.biometrics.face.AuthenticationFrame;
 import android.hardware.biometrics.face.BaseFrame;
 import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.biometrics.face.virtualhal.AcquiredInfoAndVendorCode;
+import android.hardware.biometrics.face.virtualhal.EnrollmentProgressStep;
+import android.hardware.biometrics.face.virtualhal.NextEnrollment;
 import android.hardware.face.Face;
 import android.hardware.face.FaceAuthenticationFrame;
 import android.hardware.face.FaceEnrollFrame;
@@ -50,6 +53,7 @@
 public class BiometricTestSessionImpl extends ITestSession.Stub {
 
     private static final String TAG = "face/aidl/BiometricTestSessionImpl";
+    private static final int VHAL_ENROLLMENT_ID = 9999;
 
     @NonNull private final Context mContext;
     private final int mSensorId;
@@ -144,16 +148,35 @@
 
         super.setTestHalEnabled_enforcePermission();
 
-        mProvider.setTestHalEnabled(enabled);
         mSensor.setTestHalEnabled(enabled);
+        mProvider.setTestHalEnabled(enabled);
     }
 
     @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
     @Override
-    public void startEnroll(int userId) {
+    public void startEnroll(int userId) throws RemoteException {
 
         super.startEnroll_enforcePermission();
 
+        Slog.i(TAG, "startEnroll(): isVhalForTesting=" + mProvider.isVhalForTesting());
+        if (mProvider.isVhalForTesting()) {
+            final AcquiredInfoAndVendorCode[] acquiredInfoAndVendorCodes =
+                    {new AcquiredInfoAndVendorCode()};
+            final EnrollmentProgressStep[] enrollmentProgressSteps =
+                    {new EnrollmentProgressStep(), new EnrollmentProgressStep()};
+            enrollmentProgressSteps[0].durationMs = 100;
+            enrollmentProgressSteps[0].acquiredInfoAndVendorCodes = acquiredInfoAndVendorCodes;
+            enrollmentProgressSteps[1].durationMs = 200;
+            enrollmentProgressSteps[1].acquiredInfoAndVendorCodes = acquiredInfoAndVendorCodes;
+
+            final NextEnrollment nextEnrollment = new NextEnrollment();
+            nextEnrollment.id = VHAL_ENROLLMENT_ID;
+            nextEnrollment.progressSteps = enrollmentProgressSteps;
+            nextEnrollment.result = true;
+            mProvider.getVhal().setNextEnrollment(nextEnrollment);
+            mProvider.getVhal().setOperationAuthenticateDuration(6000);
+        }
+
         mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
                 mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
                 null /* previewSurface */, false /* debugConsent */,
@@ -166,6 +189,10 @@
 
         super.finishEnroll_enforcePermission();
 
+        if (mProvider.isVhalForTesting()) {
+            return;
+        }
+
         int nextRandomId = mRandom.nextInt();
         while (mEnrollmentIds.contains(nextRandomId)) {
             nextRandomId = mRandom.nextInt();
@@ -178,11 +205,16 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
     @Override
-    public void acceptAuthentication(int userId)  {
+    public void acceptAuthentication(int userId) throws RemoteException {
 
         // Fake authentication with any of the existing faces
         super.acceptAuthentication_enforcePermission();
 
+        if (mProvider.isVhalForTesting()) {
+            mProvider.getVhal().setEnrollmentHit(VHAL_ENROLLMENT_ID);
+            return;
+        }
+
         List<Face> faces = FaceUtils.getInstance(mSensorId)
                 .getBiometricsForUser(mContext, userId);
         if (faces.isEmpty()) {
@@ -196,10 +228,15 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
     @Override
-    public void rejectAuthentication(int userId)  {
+    public void rejectAuthentication(int userId) throws RemoteException {
 
         super.rejectAuthentication_enforcePermission();
 
+        if (mProvider.isVhalForTesting()) {
+            mProvider.getVhal().setEnrollmentHit(VHAL_ENROLLMENT_ID + 1);
+            return;
+        }
+
         mSensor.getSessionForUser(userId).getHalSessionCallback().onAuthenticationFailed();
     }
 
@@ -236,11 +273,17 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
     @Override
-    public void cleanupInternalState(int userId)  {
+    public void cleanupInternalState(int userId) throws RemoteException {
 
         super.cleanupInternalState_enforcePermission();
 
         Slog.d(TAG, "cleanupInternalState: " + userId);
+
+        if (mProvider.isVhalForTesting()) {
+            Slog.i(TAG, "cleanup virtualhal configurations");
+            mProvider.getVhal().resetConfigurations(); //setEnrollments(new int[]{});
+        }
+
         mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index bb213bf..5127e68 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -16,6 +16,9 @@
 
 package com.android.server.biometrics.sensors.face.aidl;
 
+import static android.hardware.face.FaceSensorConfigurations.getIFace;
+import static android.hardware.face.FaceSensorConfigurations.remapFqName;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -32,6 +35,7 @@
 import android.hardware.biometrics.ITestSessionCallback;
 import android.hardware.biometrics.face.IFace;
 import android.hardware.biometrics.face.SensorProps;
+import android.hardware.biometrics.face.virtualhal.IVirtualHal;
 import android.hardware.face.Face;
 import android.hardware.face.FaceAuthenticateOptions;
 import android.hardware.face.FaceEnrollOptions;
@@ -54,6 +58,7 @@
 import com.android.server.biometrics.AuthenticationStatsCollector;
 import com.android.server.biometrics.BiometricDanglingReceiver;
 import com.android.server.biometrics.BiometricHandlerProvider;
+import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
@@ -130,6 +135,11 @@
     private AuthenticationStatsCollector mAuthenticationStatsCollector;
     @Nullable
     private IFace mDaemon;
+    @Nullable
+    private IVirtualHal mVhal;
+    @Nullable
+    private String mHalInstanceNameCurrent;
+
 
     private final class BiometricTaskStackListener extends TaskStackListener {
         @Override
@@ -286,14 +296,37 @@
         if (mTestHalEnabled) {
             return true;
         }
-        return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + mHalInstanceName) != null;
+        return ServiceManager.checkService(
+                remapFqName(IFace.DESCRIPTOR + "/" + mHalInstanceName)) != null;
     }
 
     @Nullable
     @VisibleForTesting
     synchronized IFace getHalInstance() {
         if (mTestHalEnabled) {
-            return new TestHal();
+            if (Flags.useVhalForTesting()) {
+                if (!mHalInstanceNameCurrent.contains("virtual")) {
+                    Slog.i(getTag(), "Switching face hal from " + mHalInstanceName
+                            + " to virtual hal");
+                    mHalInstanceNameCurrent = "virtual";
+                    mDaemon = null;
+                }
+            } else {
+                // Enabling the test HAL for a single sensor in a multi-sensor HAL currently enables
+                // the test HAL for all sensors under that HAL. This can be updated in the future if
+                // necessary.
+                return new TestHal();
+            }
+        } else {
+            if (mHalInstanceNameCurrent == null) {
+                mHalInstanceNameCurrent = mHalInstanceName;
+            } else if (mHalInstanceNameCurrent.contains("virtual")
+                    && mHalInstanceNameCurrent != mHalInstanceName) {
+                Slog.i(getTag(), "Switching face from virtual hal " + "to "
+                        + mHalInstanceName);
+                mHalInstanceNameCurrent = mHalInstanceName;
+                mDaemon = null;
+            }
         }
 
         if (mDaemon != null) {
@@ -302,10 +335,7 @@
 
         Slog.d(getTag(), "Daemon was null, reconnecting");
 
-        mDaemon = IFace.Stub.asInterface(
-                Binder.allowBlocking(
-                        ServiceManager.waitForDeclaredService(
-                                IFace.DESCRIPTOR + "/" + mHalInstanceName)));
+        mDaemon = getIFace(IFace.DESCRIPTOR + "/" + mHalInstanceNameCurrent);
         if (mDaemon == null) {
             Slog.e(getTag(), "Unable to get daemon");
             return null;
@@ -833,7 +863,13 @@
     }
 
     void setTestHalEnabled(boolean enabled) {
+        final boolean changed = enabled != mTestHalEnabled;
         mTestHalEnabled = enabled;
+        Slog.i(getTag(), "setTestHalEnabled(): isVhalForTestingFlags=" + Flags.useVhalForTesting()
+                + " mTestHalEnabled=" + mTestHalEnabled + " changed=" + changed);
+        if (changed && isVhalForTesting()) {
+            getHalInstance();
+        }
     }
 
     @Override
@@ -851,9 +887,40 @@
     }
 
     /**
+     * Return true if vhal_for_testing feature is enabled and test is active
+     */
+    public boolean isVhalForTesting() {
+        return (Flags.useVhalForTesting() && mTestHalEnabled);
+    }
+
+
+    /**
      * Sends a face re enroll notification.
      */
     public void sendFaceReEnrollNotification() {
         mAuthenticationStatsCollector.sendFaceReEnrollNotification();
     }
+
+    /**
+     * Sends a fingerprint enroll notification.
+     */
+    public void sendFingerprintReEnrollNotification() {
+        mAuthenticationStatsCollector.sendFingerprintReEnrollNotification();
+    }
+
+    /**
+     * Return virtual hal AIDL interface if it is used for testing
+     *
+     */
+    public IVirtualHal getVhal() throws RemoteException {
+        if (mVhal == null && isVhalForTesting()) {
+            mVhal = IVirtualHal.Stub.asInterface(
+                    Binder.allowBlocking(
+                            ServiceManager.waitForService(
+                                    IVirtualHal.DESCRIPTOR + "/"
+                                            + mHalInstanceNameCurrent)));
+            Slog.d(getTag(), "getVhal " + mHalInstanceNameCurrent);
+        }
+        return mVhal;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 6f95349..9fddcfc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.sensors.face.aidl;
 
 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE;
+import static android.hardware.face.FaceSensorConfigurations.remapFqName;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -337,7 +338,8 @@
         if (mTestHalEnabled) {
             return true;
         }
-        return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + halInstanceName) != null;
+        return ServiceManager.checkService(
+                remapFqName(IFace.DESCRIPTOR + "/" + halInstanceName)) != null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 86015ac..7740411 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1279,8 +1279,9 @@
     private boolean shouldApplyDozeScaleFactor() {
         // We don't apply the doze scale factor if we have a designated brightness curve for doze.
         return (mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
-                ? !mUseNormalBrightnessForDoze && mDisplayPolicy == POLICY_DOZE
-                : Display.isDozeState(mDisplayState)) && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
+                ? (!mUseNormalBrightnessForDoze && mDisplayPolicy == POLICY_DOZE)
+                        || Display.isDozeState(mDisplayState) : Display.isDozeState(mDisplayState))
+                && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
     }
 
     private class ShortTermModel {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index dc263c5..af9c9ac 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -1080,7 +1080,7 @@
      */
     public float[] getNits() {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mNits;
+            return mEvenDimmerBrightnessData.nits;
         }
         return mNits;
     }
@@ -1093,7 +1093,7 @@
     @VisibleForTesting
     public float[] getBacklight() {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mBacklight;
+            return mEvenDimmerBrightnessData.backlight;
         }
         return mBacklight;
     }
@@ -1107,7 +1107,7 @@
      */
     public float getBacklightFromBrightness(float brightness) {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mBrightnessToBacklight.interpolate(brightness);
+            return mEvenDimmerBrightnessData.brightnessToBacklight.interpolate(brightness);
         }
         return mBrightnessToBacklightSpline.interpolate(brightness);
     }
@@ -1120,7 +1120,7 @@
      */
     public float getBrightnessFromBacklight(float backlight) {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mBacklightToBrightness.interpolate(backlight);
+            return mEvenDimmerBrightnessData.backlightToBrightness.interpolate(backlight);
         }
         return mBacklightToBrightnessSpline.interpolate(backlight);
     }
@@ -1131,7 +1131,7 @@
      */
     private Spline getBacklightToBrightnessSpline() {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mBacklightToBrightness;
+            return mEvenDimmerBrightnessData.backlightToBrightness;
         }
         return mBacklightToBrightnessSpline;
     }
@@ -1144,11 +1144,11 @@
      */
     public float getNitsFromBacklight(float backlight) {
         if (mEvenDimmerBrightnessData != null) {
-            if (mEvenDimmerBrightnessData.mBacklightToNits == null) {
+            if (mEvenDimmerBrightnessData.backlightToNits == null) {
                 return INVALID_NITS;
             }
             backlight = Math.max(backlight, mBacklightMinimum);
-            return mEvenDimmerBrightnessData.mBacklightToNits.interpolate(backlight);
+            return mEvenDimmerBrightnessData.backlightToNits.interpolate(backlight);
         }
 
         if (mBacklightToNitsSpline == null) {
@@ -1165,14 +1165,14 @@
      */
     public float getBacklightFromNits(float nits) {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mNitsToBacklight.interpolate(nits);
+            return mEvenDimmerBrightnessData.nitsToBacklight.interpolate(nits);
         }
         return mNitsToBacklightSpline.interpolate(nits);
     }
 
     private Spline getNitsToBacklightSpline() {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mNitsToBacklight;
+            return mEvenDimmerBrightnessData.nitsToBacklight;
         }
         return mNitsToBacklightSpline;
     }
@@ -1186,7 +1186,7 @@
         if (mEvenDimmerBrightnessData == null) {
             return INVALID_NITS;
         }
-        return mEvenDimmerBrightnessData.mMinLuxToNits.interpolate(lux);
+        return mEvenDimmerBrightnessData.minLuxToNits.interpolate(lux);
     }
 
     /**
@@ -1197,7 +1197,7 @@
         if (mEvenDimmerBrightnessData == null) {
             return PowerManager.BRIGHTNESS_MIN;
         }
-        return mEvenDimmerBrightnessData.mTransitionPoint;
+        return mEvenDimmerBrightnessData.transitionPoint;
     }
 
     /**
@@ -1268,7 +1268,7 @@
      */
     public float[] getBrightness() {
         if (mEvenDimmerBrightnessData != null) {
-            return mEvenDimmerBrightnessData.mBrightness;
+            return mEvenDimmerBrightnessData.brightness;
         }
         return mBrightness;
     }
@@ -2617,13 +2617,13 @@
                 List<NonNegativeFloatToFloatPoint> points = map.getMap().getPoint();
                 for (NonNegativeFloatToFloatPoint point : points) {
                     float lux = point.getFirst().floatValue();
-                    float maxBrightness = point.getSecond().floatValue();
-                    if (maxBrightness > hbmTransitionPoint) {
+                    float maxBacklight = point.getSecond().floatValue();
+                    if (maxBacklight > hbmTransitionPoint) {
                         Slog.wtf(TAG,
-                                "Invalid NBM config: maxBrightness is greater than hbm"
+                                "Invalid NBM config: maxBacklight is greater than hbm"
                                         + ".transitionPoint. type="
-                                        + type + "; lux=" + lux + "; maxBrightness="
-                                        + maxBrightness);
+                                        + type + "; lux=" + lux + "; maxBacklight="
+                                        + maxBacklight);
                         continue;
                     }
                     if (luxToTransitionPointMap.containsKey(lux)) {
@@ -2632,8 +2632,7 @@
                                         + lux);
                         continue;
                     }
-                    luxToTransitionPointMap.put(lux,
-                            getBrightnessFromBacklight(maxBrightness));
+                    luxToTransitionPointMap.put(lux, getBrightnessFromBacklight(maxBacklight));
                 }
                 if (!luxToTransitionPointMap.isEmpty()) {
                     mLuxThrottlingData.put(mappedType, luxToTransitionPointMap);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e7fd8f7..1f9eb08 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -571,6 +571,10 @@
     private final DisplayNotificationManager mDisplayNotificationManager;
     private final ExternalDisplayStatsService mExternalDisplayStatsService;
 
+    // Manages the relative placement of extended displays
+    @Nullable
+    private final DisplayTopologyCoordinator mDisplayTopologyCoordinator;
+
     /**
      * Applications use {@link android.view.Display#getRefreshRate} and
      * {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
@@ -644,6 +648,11 @@
         mDisplayNotificationManager = new DisplayNotificationManager(mFlags, mContext,
                 mExternalDisplayStatsService);
         mExternalDisplayPolicy = new ExternalDisplayPolicy(new ExternalDisplayPolicyInjector());
+        if (mFlags.isDisplayTopologyEnabled()) {
+            mDisplayTopologyCoordinator = new DisplayTopologyCoordinator();
+        } else {
+            mDisplayTopologyCoordinator = null;
+        }
     }
 
     public void setupSchedulerPolicies() {
@@ -3368,10 +3377,18 @@
     private void dumpInternal(PrintWriter pw) {
         pw.println("DISPLAY MANAGER (dumpsys display)");
         BrightnessTracker brightnessTrackerLocal;
+        SparseArray<DisplayPowerController> displayPowerControllersLocal = new SparseArray<>();
+        int displayPowerControllerCount;
 
         synchronized (mSyncRoot) {
             brightnessTrackerLocal = mBrightnessTracker;
 
+            displayPowerControllerCount = mDisplayPowerControllers.size();
+            for (int i = 0; i < displayPowerControllerCount; i++) {
+                displayPowerControllersLocal.put(
+                        mDisplayPowerControllers.keyAt(i), mDisplayPowerControllers.valueAt(i));
+            }
+
             pw.println("  mSafeMode=" + mSafeMode);
             pw.println("  mPendingTraversal=" + mPendingTraversal);
             pw.println("  mViewports=" + mViewports);
@@ -3442,13 +3459,6 @@
                         + ", mWifiDisplayScanRequested=" + callback.mWifiDisplayScanRequested);
             }
 
-            final int displayPowerControllerCount = mDisplayPowerControllers.size();
-            pw.println();
-            pw.println("Display Power Controllers: size=" + displayPowerControllerCount);
-            for (int i = 0; i < displayPowerControllerCount; i++) {
-                mDisplayPowerControllers.valueAt(i).dump(pw);
-            }
-
             pw.println();
             mPersistentDataStore.dump(pw);
 
@@ -3461,6 +3471,12 @@
                 mDisplayWindowPolicyControllers.valueAt(i).second.dump("  ", pw);
             }
         }
+        pw.println();
+        pw.println("Display Power Controllers: size=" + displayPowerControllerCount);
+        for (int i = 0; i < displayPowerControllerCount; i++) {
+            displayPowerControllersLocal.valueAt(i).dump(pw);
+        }
+
         if (brightnessTrackerLocal != null) {
             pw.println();
             brightnessTrackerLocal.dump(pw);
@@ -3474,9 +3490,13 @@
             mSmallAreaDetectionController.dump(pw);
         }
 
+        if (mDisplayTopologyCoordinator != null) {
+            pw.println();
+            mDisplayTopologyCoordinator.dump(pw);
+        }
+
         pw.println();
         mFlags.dump(pw);
-
     }
 
     private static float[] getFloatArray(TypedArray array) {
@@ -5280,6 +5300,17 @@
         }
 
         @Override
+        public IntArray getDisplayIds() {
+            IntArray displayIds = new IntArray();
+            synchronized (mSyncRoot) {
+                mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
+                    displayIds.add(logicalDisplay.getDisplayIdLocked());
+                }), /* includeDisabled= */ false);
+            }
+            return displayIds;
+        }
+
+        @Override
         public DisplayManagerInternal.DisplayOffloadSession registerDisplayOffloader(
                 int displayId, @NonNull DisplayManagerInternal.DisplayOffloader displayOffloader) {
             if (!mFlags.isDisplayOffloadEnabled()) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 04573f4..62d8761 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -22,6 +22,7 @@
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
+import static com.android.server.display.brightness.BrightnessEvent.FLAG_EVEN_DIMMER;
 import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessPresetToString;
 
 import android.animation.Animator;
@@ -1357,6 +1358,13 @@
                 mDisplayStateController.shouldPerformScreenOffTransition());
         state = mPowerState.getScreenState();
 
+        // Use doze brightness if one of following is true:
+        // 1. The target `state` isDozeState.
+        // 2. Doze power request(POLICY_DOZE) if there's no exception(useNormalBrightnessForDoze).
+        final boolean useDozeBrightness = mFlags.isNormalBrightnessForDozeParameterEnabled()
+                ? (!mPowerRequest.useNormalBrightnessForDoze && mPowerRequest.policy == POLICY_DOZE)
+                        || Display.isDozeState(state) : Display.isDozeState(state);
+
         DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
                 .updateBrightness(mPowerRequest, state, mDisplayOffloadSession);
         float brightnessState = displayBrightnessState.getBrightness();
@@ -1399,7 +1407,7 @@
                     && !mAutomaticBrightnessController.isInIdleMode()) {
                 // Set sendUpdate to false, we're already in updatePowerState() so there's no need
                 // to trigger it again
-                mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+                mAutomaticBrightnessController.switchMode(useDozeBrightness
                         ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT,
                         /* sendUpdate= */ false);
             }
@@ -1472,9 +1480,7 @@
             brightnessState = clampScreenBrightness(brightnessState);
         }
 
-        if (mFlags.isNormalBrightnessForDozeParameterEnabled()
-                ? !mPowerRequest.useNormalBrightnessForDoze && mPowerRequest.policy == POLICY_DOZE
-                : Display.isDozeState(state)) {
+        if (useDozeBrightness) {
             // TODO(b/329676661): Introduce a config property to choose between this brightness
             //  strategy and DOZE_DEFAULT
             // On some devices, when auto-brightness is disabled and the device is dozing, we use
@@ -1750,6 +1756,8 @@
         final float brightnessOnAvailableScale = MathUtils.constrainedMap(0.0f, 1.0f,
                 clampedState.getMinBrightness(), clampedMax,
                 brightnessState);
+        final boolean evenDimmerModeOn =
+                mCdsi != null && mCdsi.getReduceBrightColorsActivatedForEvenDimmer();
         mTempBrightnessEvent.setPercent(Math.round(
                 1000.0f * com.android.internal.display.BrightnessUtils.convertLinearToGamma(
                         brightnessOnAvailableScale) / 10)); // rounded to one dp
@@ -1764,7 +1772,8 @@
         mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode());
         mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
                 | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0)
-                | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0));
+                | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0)
+                | (evenDimmerModeOn ? FLAG_EVEN_DIMMER : 0));
         mTempBrightnessEvent.setRbcStrength(mCdsi != null
                 ? mCdsi.getReduceBrightColorsStrength() : -1);
         mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
new file mode 100644
index 0000000..631f147
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.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.server.display;
+
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class manages the relative placement (topology) of extended displays. It is responsible for
+ * updating and persisting the topology.
+ */
+class DisplayTopologyCoordinator {
+
+    /**
+     * The topology tree
+     */
+    @Nullable
+    private TopologyTreeNode mRoot;
+
+    /**
+     * The logical display ID of the primary display that will show certain UI elements.
+     * This is not necessarily the same as the default display.
+     */
+    private int mPrimaryDisplayId;
+
+    /**
+     * Print the object's state and debug information into the given stream.
+     * @param pw The stream to dump information to.
+     */
+    public void dump(PrintWriter pw) {
+        pw.println("DisplayTopologyCoordinator:");
+        pw.println("--------------------");
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+        ipw.increaseIndent();
+
+        ipw.println("mPrimaryDisplayId: " + mPrimaryDisplayId);
+
+        ipw.println("Topology tree:");
+        if (mRoot != null) {
+            ipw.increaseIndent();
+            mRoot.dump(ipw);
+            ipw.decreaseIndent();
+        }
+    }
+
+    private static class TopologyTreeNode {
+
+        /**
+         * The logical display ID
+         */
+        private int mDisplayId;
+
+        private final List<TopologyTreeNode> mChildren = new ArrayList<>();
+
+        /**
+         * The position of this display relative to its parent.
+         */
+        private Position mPosition;
+
+        /**
+         * The distance from the top edge of the parent 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).
+         */
+        private double mOffset;
+
+        /**
+         * Print the object's state and debug information into the given stream.
+         * @param ipw The stream to dump information to.
+         */
+        void dump(IndentingPrintWriter ipw) {
+            ipw.println("Display {id=" + mDisplayId + ", position=" + mPosition
+                    + ", offset=" + mOffset + "}");
+            ipw.increaseIndent();
+            for (TopologyTreeNode child : mChildren) {
+                child.dump(ipw);
+            }
+            ipw.decreaseIndent();
+        }
+
+        private enum Position {
+            POSITION_LEFT, POSITION_TOP, POSITION_RIGHT, POSITION_BOTTOM
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 0570b2a..5edea0a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -37,9 +37,9 @@
 import android.os.Trace;
 import android.util.DisplayUtils;
 import android.util.LongSparseArray;
-import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.Spline;
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.DisplayCutout;
@@ -81,10 +81,6 @@
     private static final String UNIQUE_ID_PREFIX = "local:";
 
     private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.boot.emulator.circular";
-    // Min and max strengths for even dimmer feature.
-    private static final float EVEN_DIMMER_MIN_STRENGTH = 0.0f;
-    private static final float EVEN_DIMMER_MAX_STRENGTH = 90.0f;
-    private static final float BRIGHTNESS_MIN = 0.0f;
 
     private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();
 
@@ -99,7 +95,9 @@
     private Context mOverlayContext;
 
     private int mEvenDimmerStrength = -1;
+    private boolean mEvenDimmerEnabled = false;
     private ColorDisplayService.ColorDisplayServiceInternal mCdsi;
+    private Spline mNitsToEvenDimmerStrength;
 
     // Called with SyncRoot lock held.
     LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context,
@@ -279,7 +277,7 @@
             mIsFirstDisplay = isFirstDisplay;
             updateDisplayPropertiesLocked(staticDisplayInfo, dynamicInfo, modeSpecs);
             mSidekickInternal = LocalServices.getService(SidekickInternal.class);
-            mBacklightAdapter = new BacklightAdapter(displayToken, isFirstDisplay,
+            mBacklightAdapter = mInjector.getBacklightAdapter(displayToken, isFirstDisplay,
                     mSurfaceControlProxy);
             mActiveSfDisplayModeAtStartId = dynamicInfo.activeDisplayModeId;
         }
@@ -998,26 +996,36 @@
                     }
 
                     private void applyColorMatrixBasedDimming(float brightnessState) {
-                        int strength = (int) (MathUtils.constrainedMap(
-                                // to this range:
-                                EVEN_DIMMER_MAX_STRENGTH, EVEN_DIMMER_MIN_STRENGTH,
-                                // from this range:
-                                BRIGHTNESS_MIN, mDisplayDeviceConfig.getEvenDimmerTransitionPoint(),
-                                // map this (+ rounded up):
-                                brightnessState) + 0.5);
-
-                        if (mEvenDimmerStrength < 0 // uninitialised
-                                || MathUtils.abs(mEvenDimmerStrength - strength) > 1
-                                || strength <= 1) {
-                            mEvenDimmerStrength = strength;
-                        }
-                        boolean enabled = mEvenDimmerStrength > 0.0f;
-
                         if (mCdsi == null) {
                             mCdsi = LocalServices.getService(
                                     ColorDisplayService.ColorDisplayServiceInternal.class);
                         }
-                        if (mCdsi != null) {
+                        if (mCdsi == null) {
+                            return;
+                        }
+
+                        final float minHardwareNits = backlightToNits(brightnessToBacklight(
+                                mDisplayDeviceConfig.getEvenDimmerTransitionPoint()));
+                        final float requestedNits =
+                                backlightToNits(brightnessToBacklight(brightnessState));
+                        mNitsToEvenDimmerStrength =
+                                mCdsi.fetchEvenDimmerSpline(minHardwareNits);
+
+                        if (mNitsToEvenDimmerStrength == null) {
+                            return;
+                        }
+
+                        // Find required dimming strength, rounded up.
+                        int strength = Math.round(mNitsToEvenDimmerStrength
+                                .interpolate(requestedNits));
+                        boolean enabled = strength > 0.0f;
+                        if (mEvenDimmerEnabled != enabled) {
+                            Slog.i(TAG, "Setting Extra Dim; strength: " + strength
+                                    + ", " + (enabled ? "enabled" : "disabled"));
+                        }
+                        if (mEvenDimmerStrength != strength || mEvenDimmerEnabled != enabled) {
+                            mEvenDimmerEnabled = enabled;
+                            mEvenDimmerStrength = strength;
                             mCdsi.applyEvenDimmerColorChanges(enabled, strength);
                         }
                     }
@@ -1290,6 +1298,9 @@
             pw.println("DisplayDeviceConfig: ");
             pw.println("---------------------");
             pw.println(mDisplayDeviceConfig);
+            pw.println("mEvenDimmerEnabled=" + mEvenDimmerEnabled);
+            pw.println("mEvenDimmerStrength=" + mEvenDimmerStrength);
+            pw.println("mNitsToEvenDimmerStrength=" + mNitsToEvenDimmerStrength);
         }
 
         private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
@@ -1461,6 +1472,12 @@
                 long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
             return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay, flags);
         }
+
+        public BacklightAdapter getBacklightAdapter(IBinder displayToken, boolean isFirstDisplay,
+                SurfaceControlProxy surfaceControlProxy) {
+            return new BacklightAdapter(displayToken, isFirstDisplay, surfaceControlProxy);
+
+        }
     }
 
     public interface DisplayEventListener {
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index ad57ebf..9e9b899 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -44,6 +44,7 @@
     public static final int FLAG_DOZE_SCALE = 0x4;
     public static final int FLAG_USER_SET = 0x8;
     public static final int FLAG_LOW_POWER_MODE = 0x20;
+    public static final int FLAG_EVEN_DIMMER = 0x40;
 
     private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
@@ -492,6 +493,7 @@
                 + ((mFlags & FLAG_RBC) != 0 ? "rbc " : "")
                 + ((mFlags & FLAG_INVALID_LUX) != 0 ? "invalid_lux " : "")
                 + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "")
-                + ((mFlags & FLAG_LOW_POWER_MODE) != 0 ? "low_power_mode " : "");
+                + ((mFlags & FLAG_LOW_POWER_MODE) != 0 ? "low_power_mode " : "")
+                + ((mFlags & FLAG_EVEN_DIMMER) != 0 ? "even_dimmer " : "");
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index cf44ac0..a1fd164 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -74,6 +74,5 @@
 
     protected enum Type {
         POWER,
-        WEAR_BEDTIME_MODE,
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 9404034..a10094f 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -218,9 +218,7 @@
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
         } else if (mClamperType == Type.POWER) {
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
-        } else if (mClamperType == Type.WEAR_BEDTIME_MODE) {
-            return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE;
-        } else {
+        }  else {
             Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType);
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
         }
@@ -350,10 +348,6 @@
                             data, currentBrightness));
                 }
             }
-            if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
-                clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
-                        clamperChangeListener, data));
-            }
             return clampers;
         }
 
@@ -362,6 +356,10 @@
                 DisplayDeviceData data) {
             List<BrightnessStateModifier> modifiers = new ArrayList<>();
             modifiers.add(new BrightnessThermalModifier(handler, listener, data));
+            if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
+                modifiers.add(new BrightnessWearBedtimeModeModifier(handler, context,
+                        listener, data));
+            }
 
             modifiers.add(new DisplayDimModifier(context));
             modifiers.add(new BrightnessLowPowerModeModifier());
@@ -395,7 +393,7 @@
      */
     public static class DisplayDeviceData implements BrightnessThermalModifier.ThermalData,
             BrightnessPowerClamper.PowerData,
-            BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
+            BrightnessWearBedtimeModeModifier.WearBedtimeModeData {
         @NonNull
         private final String mUniqueDisplayId;
         @NonNull
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
deleted file mode 100644
index 1902e35..0000000
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display.brightness.clamper;
-
-import android.annotation.NonNull;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-public class BrightnessWearBedtimeModeClamper extends
-        BrightnessClamper<BrightnessWearBedtimeModeClamper.WearBedtimeModeData> {
-
-    public static final int BEDTIME_MODE_OFF = 0;
-    public static final int BEDTIME_MODE_ON = 1;
-
-    private final Context mContext;
-
-    private final ContentObserver mSettingsObserver;
-
-    BrightnessWearBedtimeModeClamper(Handler handler, Context context,
-            BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
-        this(new Injector(), handler, context, listener, data);
-    }
-
-    @VisibleForTesting
-    BrightnessWearBedtimeModeClamper(Injector injector, Handler handler, Context context,
-            BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
-        super(handler, listener);
-        mContext = context;
-        mBrightnessCap = data.getBrightnessWearBedtimeModeCap();
-        mSettingsObserver = new ContentObserver(mHandler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                final int bedtimeModeSetting = Settings.Global.getInt(
-                        mContext.getContentResolver(),
-                        Settings.Global.Wearable.BEDTIME_MODE,
-                        BEDTIME_MODE_OFF);
-                mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON;
-                mChangeListener.onChanged();
-            }
-        };
-        injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver);
-    }
-
-    @NonNull
-    @Override
-    Type getType() {
-        return Type.WEAR_BEDTIME_MODE;
-    }
-
-    @Override
-    void onDeviceConfigChanged() {}
-
-    @Override
-    void onDisplayChanged(WearBedtimeModeData displayData) {
-        mHandler.post(() -> {
-            mBrightnessCap = displayData.getBrightnessWearBedtimeModeCap();
-            mChangeListener.onChanged();
-        });
-    }
-
-    @Override
-    void stop() {
-        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
-    }
-
-    interface WearBedtimeModeData {
-        float getBrightnessWearBedtimeModeCap();
-    }
-
-    @VisibleForTesting
-    static class Injector {
-        void registerBedtimeModeObserver(@NonNull ContentResolver cr,
-                @NonNull ContentObserver observer) {
-            cr.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE),
-                    /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifier.java
new file mode 100644
index 0000000..c9c8c33
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifier.java
@@ -0,0 +1,158 @@
+/*
+ * 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.server.display.brightness.clamper;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+
+import java.io.PrintWriter;
+
+public class BrightnessWearBedtimeModeModifier implements BrightnessStateModifier,
+        BrightnessClamperController.DisplayDeviceDataListener,
+        BrightnessClamperController.StatefulModifier {
+
+    public static final int BEDTIME_MODE_OFF = 0;
+    public static final int BEDTIME_MODE_ON = 1;
+
+    private final Context mContext;
+
+    private final ContentObserver mSettingsObserver;
+    protected final Handler mHandler;
+    protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
+
+    private float mBrightnessCap;
+    private boolean mIsActive = false;
+    private boolean mApplied = false;
+
+    BrightnessWearBedtimeModeModifier(Handler handler, Context context,
+            BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+        this(new Injector(), handler, context, listener, data);
+    }
+
+    @VisibleForTesting
+    BrightnessWearBedtimeModeModifier(Injector injector, Handler handler, Context context,
+            BrightnessClamperController.ClamperChangeListener listener, WearBedtimeModeData data) {
+        mHandler = handler;
+        mChangeListener = listener;
+        mContext = context;
+        mBrightnessCap = data.getBrightnessWearBedtimeModeCap();
+        mSettingsObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                final int bedtimeModeSetting = Settings.Global.getInt(
+                        mContext.getContentResolver(),
+                        Settings.Global.Wearable.BEDTIME_MODE,
+                        BEDTIME_MODE_OFF);
+                mIsActive = bedtimeModeSetting == BEDTIME_MODE_ON;
+                mChangeListener.onChanged();
+            }
+        };
+        injector.registerBedtimeModeObserver(context.getContentResolver(), mSettingsObserver);
+    }
+
+    //region BrightnessStateModifier
+    @Override
+    public void apply(DisplayManagerInternal.DisplayPowerRequest request,
+            DisplayBrightnessState.Builder stateBuilder) {
+        if (mIsActive && stateBuilder.getMaxBrightness() > mBrightnessCap) {
+            stateBuilder.setMaxBrightness(mBrightnessCap);
+            stateBuilder.setBrightness(Math.min(stateBuilder.getBrightness(), mBrightnessCap));
+            stateBuilder.setBrightnessMaxReason(
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE);
+            stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
+            // set fast change only when modifier is activated.
+            // this will allow auto brightness to apply slow change even when modifier is active
+            if (!mApplied) {
+                stateBuilder.setIsSlowChange(false);
+            }
+            mApplied = true;
+        } else {
+            mApplied = false;
+        }
+    }
+
+    @Override
+    public void stop() {
+        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+        writer.println("BrightnessWearBedtimeModeModifier:");
+        writer.println("  mBrightnessCap: " + mBrightnessCap);
+        writer.println("  mIsActive: " + mIsActive);
+        writer.println("  mApplied: " + mApplied);
+    }
+
+    @Override
+    public boolean shouldListenToLightSensor() {
+        return false;
+    }
+
+    @Override
+    public void setAmbientLux(float lux) {
+        // noop
+    }
+    //endregion
+
+    //region DisplayDeviceDataListener
+    @Override
+    public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData data) {
+        mHandler.post(() -> {
+            mBrightnessCap = data.getBrightnessWearBedtimeModeCap();
+            mChangeListener.onChanged();
+        });
+    }
+    //endregion
+
+    //region StatefulModifier
+    @Override
+    public void applyStateChange(
+            BrightnessClamperController.ModifiersAggregatedState aggregatedState) {
+        if (mIsActive && aggregatedState.mMaxBrightness > mBrightnessCap) {
+            aggregatedState.mMaxBrightness = mBrightnessCap;
+            aggregatedState.mMaxBrightnessReason =
+                    BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE;
+        }
+    }
+    //endregion
+
+    interface WearBedtimeModeData {
+        float getBrightnessWearBedtimeModeCap();
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE),
+                    /* notifyForDescendants= */ false, observer, UserHandle.USER_ALL);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index bf01f2d..2fdec78 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -510,10 +510,10 @@
                 && mAutomaticBrightnessController != null
                 && !mAutomaticBrightnessController.isInIdleMode()) {
 
-            boolean shouldUseDozeMode =
+            final boolean shouldUseDozeMode =
                     mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
-                            ? !useNormalBrightnessForDoze && policy == POLICY_DOZE
-                            : Display.isDozeState(state);
+                            ? (!useNormalBrightnessForDoze && policy == POLICY_DOZE)
+                                    || Display.isDozeState(state) : Display.isDozeState(state);
             mAutomaticBrightnessController.switchMode(shouldUseDozeMode
                     ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT, sendUpdate);
         }
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index bd759f3..dc59e66 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -70,6 +70,7 @@
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.Spline;
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.accessibility.AccessibilityManager;
@@ -114,6 +115,8 @@
         Matrix.setIdentityM(MATRIX_IDENTITY, 0);
     }
 
+    private static final int EVEN_DIMMER_MAX_PERCENT_ALLOWED = 100;
+
     private static final int MSG_USER_CHANGED = 0;
     private static final int MSG_SET_UP = 1;
     private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 2;
@@ -193,6 +196,9 @@
     private final boolean mVisibleBackgroundUsersEnabled;
     private final UserManagerService mUserManager;
 
+    private Spline mEvenDimmerSpline;
+    private boolean mEvenDimmerActivated;
+
     public ColorDisplayService(Context context) {
         super(context);
         mHandler = new TintHandler(DisplayThread.get().getLooper());
@@ -1625,6 +1631,16 @@
         }
 
         /**
+         * Gets the adjusted nits, given a strength and nits.
+         * @param strength of reduce bright colors
+         * @param nits target nits
+         * @return the actual nits that would be output, given input nits and rbc strength.
+         */
+        public float getAdjustedNitsForStrength(float nits, int strength) {
+            return mReduceBrightColorsTintController.getAdjustedNitsForStrength(nits, strength);
+        }
+
+        /**
          * Sets the listener and returns whether reduce bright colors is currently enabled.
          */
         public boolean setReduceBrightColorsListener(ReduceBrightColorsListener listener) {
@@ -1644,6 +1660,14 @@
         }
 
         /**
+         *
+         * @return whether reduce bright colors is on, due to even dimmer being activated
+         */
+        public boolean getReduceBrightColorsActivatedForEvenDimmer() {
+            return mEvenDimmerActivated;
+        }
+
+        /**
          * Gets the computed brightness, in nits, when the reduce bright colors feature is applied
          * at the current strength.
          *
@@ -1667,10 +1691,42 @@
          * Applies tint changes for even dimmer feature.
          */
         public void applyEvenDimmerColorChanges(boolean enabled, int strength) {
+            mEvenDimmerActivated = enabled;
             mReduceBrightColorsTintController.setActivated(enabled);
             mReduceBrightColorsTintController.setMatrix(strength);
             mHandler.sendEmptyMessage(MSG_APPLY_REDUCE_BRIGHT_COLORS);
         }
+
+        /**
+         * Get spline to map between requested nits, and required even dimmer strength.
+         * @return nits to strength spline
+         */
+        public Spline fetchEvenDimmerSpline(float nits) {
+            if (mEvenDimmerSpline == null) {
+                mEvenDimmerSpline = createNitsToStrengthSpline(nits);
+            }
+            return mEvenDimmerSpline;
+        }
+
+        /**
+         * Creates a spline mapping requested nits values, for each resulting strength value
+         * (in percent) for even dimmer.
+         * Returns null if coefficients are not initialised.
+         * @return spline from nits to strength
+         */
+        private Spline createNitsToStrengthSpline(float nits) {
+            final float[] requestedNits = new float[EVEN_DIMMER_MAX_PERCENT_ALLOWED + 1];
+            final float[] resultingStrength = new float[EVEN_DIMMER_MAX_PERCENT_ALLOWED + 1];
+            for (int i = 0; i <= EVEN_DIMMER_MAX_PERCENT_ALLOWED; i++) {
+                resultingStrength[EVEN_DIMMER_MAX_PERCENT_ALLOWED - i] = i;
+                requestedNits[EVEN_DIMMER_MAX_PERCENT_ALLOWED - i] =
+                        getAdjustedNitsForStrength(nits, i);
+                if (requestedNits[EVEN_DIMMER_MAX_PERCENT_ALLOWED - i] == 0) {
+                    return null;
+                }
+            }
+            return new Spline.LinearSpline(requestedNits, resultingStrength);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
index f529c4c..4f6fbd0 100644
--- a/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
+++ b/services/core/java/com/android/server/display/color/ReduceBrightColorsTintController.java
@@ -123,6 +123,14 @@
         return computeComponentValue(mStrength) * nits;
     }
 
+    /**
+     * Returns the effective brightness (in nits), which has been adjusted to account for the effect
+     * of the bright color reduction.
+     */
+    public float getAdjustedNitsForStrength(float nits, int strength) {
+        return computeComponentValue(strength) * nits;
+    }
+
     private float computeComponentValue(int strengthLevel) {
         final float percentageStrength = strengthLevel / 100f;
         final float squaredPercentageStrength = percentageStrength * percentageStrength;
diff --git a/services/core/java/com/android/server/display/config/EvenDimmerBrightnessData.java b/services/core/java/com/android/server/display/config/EvenDimmerBrightnessData.java
index 7e2e10a..9a590a2 100644
--- a/services/core/java/com/android/server/display/config/EvenDimmerBrightnessData.java
+++ b/services/core/java/com/android/server/display/config/EvenDimmerBrightnessData.java
@@ -34,72 +34,76 @@
     /**
      * Brightness value at which even dimmer methods are used.
      */
-    public final float mTransitionPoint;
+    public final float transitionPoint;
 
     /**
      * Nits array, maps to mBacklight
      */
-    public final float[] mNits;
+    public final float[] nits;
 
     /**
      * Backlight array, maps to mBrightness and mNits
      */
-    public final float[] mBacklight;
+    public final float[] backlight;
 
     /**
      * Brightness array, maps to mBacklight
      */
-    public final float[] mBrightness;
+    public final float[] brightness;
+
     /**
      * Spline, mapping between backlight and nits
      */
-    public final Spline mBacklightToNits;
+    public final Spline backlightToNits;
+
     /**
      * Spline, mapping between nits and backlight
      */
-    public final Spline mNitsToBacklight;
+    public final Spline nitsToBacklight;
+
     /**
      * Spline, mapping between brightness and backlight
      */
-    public final Spline mBrightnessToBacklight;
+    public final Spline brightnessToBacklight;
+
     /**
      * Spline, mapping between backlight and brightness
      */
-    public final Spline mBacklightToBrightness;
+    public final Spline backlightToBrightness;
 
     /**
      * Spline, mapping the minimum nits for each lux condition.
      */
-    public final Spline mMinLuxToNits;
+    public final Spline minLuxToNits;
 
     @VisibleForTesting
     public EvenDimmerBrightnessData(float transitionPoint, float[] nits,
             float[] backlight, float[] brightness, Spline backlightToNits,
             Spline nitsToBacklight, Spline brightnessToBacklight, Spline backlightToBrightness,
             Spline minLuxToNits) {
-        mTransitionPoint = transitionPoint;
-        mNits = nits;
-        mBacklight = backlight;
-        mBrightness = brightness;
-        mBacklightToNits = backlightToNits;
-        mNitsToBacklight = nitsToBacklight;
-        mBrightnessToBacklight = brightnessToBacklight;
-        mBacklightToBrightness = backlightToBrightness;
-        mMinLuxToNits = minLuxToNits;
+        this.transitionPoint = transitionPoint;
+        this.nits = nits;
+        this.backlight = backlight;
+        this.brightness = brightness;
+        this.backlightToNits = backlightToNits;
+        this.nitsToBacklight = nitsToBacklight;
+        this.brightnessToBacklight = brightnessToBacklight;
+        this.backlightToBrightness = backlightToBrightness;
+        this.minLuxToNits = minLuxToNits;
     }
 
     @Override
     public String toString() {
         return "EvenDimmerBrightnessData {"
-                + "mTransitionPoint: " + mTransitionPoint
-                + ", mNits: " + Arrays.toString(mNits)
-                + ", mBacklight: " + Arrays.toString(mBacklight)
-                + ", mBrightness: " + Arrays.toString(mBrightness)
-                + ", mBacklightToNits: " + mBacklightToNits
-                + ", mNitsToBacklight: " + mNitsToBacklight
-                + ", mBrightnessToBacklight: " + mBrightnessToBacklight
-                + ", mBacklightToBrightness: " + mBacklightToBrightness
-                + ", mMinLuxToNits: " + mMinLuxToNits
+                + "transitionPoint: " + transitionPoint
+                + ", nits: " + Arrays.toString(nits)
+                + ", backlight: " + Arrays.toString(backlight)
+                + ", brightness: " + Arrays.toString(brightness)
+                + ", backlightToNits: " + backlightToNits
+                + ", nitsToBacklight: " + nitsToBacklight
+                + ", brightnessToBacklight: " + brightnessToBacklight
+                + ", backlightToBrightness: " + backlightToBrightness
+                + ", minLuxToNits: " + minLuxToNits
                 + "} ";
     }
 
@@ -122,7 +126,6 @@
         if (map == null) {
             return null;
         }
-        String interpolation = map.getInterpolation();
 
         List<BrightnessPoint> brightnessPoints = map.getBrightnessPoint();
         if (brightnessPoints.isEmpty()) {
@@ -169,22 +172,11 @@
             ++i;
         }
 
-        // Explicitly choose linear interpolation.
-        if ("linear".equals(interpolation)) {
-            return new EvenDimmerBrightnessData(transitionPoint, nits, backlight, brightness,
-                    new Spline.LinearSpline(backlight, nits),
-                    new Spline.LinearSpline(nits, backlight),
-                    new Spline.LinearSpline(brightness, backlight),
-                    new Spline.LinearSpline(backlight, brightness),
-                    new Spline.LinearSpline(minLux, minNits)
-            );
-        }
-
         return new EvenDimmerBrightnessData(transitionPoint, nits, backlight, brightness,
-                Spline.createSpline(backlight, nits),
-                Spline.createSpline(nits, backlight),
-                Spline.createSpline(brightness, backlight),
-                Spline.createSpline(backlight, brightness),
+                new Spline.LinearSpline(backlight, nits),
+                new Spline.LinearSpline(nits, backlight),
+                new Spline.LinearSpline(brightness, backlight),
+                new Spline.LinearSpline(backlight, brightness),
                 Spline.createSpline(minLux, minNits)
         );
     }
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 f600e7f..df66893 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -69,6 +69,10 @@
             Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY,
             Flags::enableModeLimitForExternalDisplay);
 
+    private final FlagState mDisplayTopology = new FlagState(
+            Flags.FLAG_DISPLAY_TOPOLOGY,
+            Flags::displayTopology);
+
     private final FlagState mConnectedDisplayErrorHandlingFlagState = new FlagState(
             Flags.FLAG_ENABLE_CONNECTED_DISPLAY_ERROR_HANDLING,
             Flags::enableConnectedDisplayErrorHandling);
@@ -266,6 +270,10 @@
         return mExternalDisplayLimitModeState.isEnabled();
     }
 
+    public boolean isDisplayTopologyEnabled() {
+        return mDisplayTopology.isEnabled();
+    }
+
     /**
      * @return Whether displays refresh rate synchronization is enabled.
      */
@@ -441,6 +449,7 @@
         pw.println(" " + mConnectedDisplayManagementFlagState);
         pw.println(" " + mDisplayOffloadFlagState);
         pw.println(" " + mExternalDisplayLimitModeState);
+        pw.println(" " + mDisplayTopology);
         pw.println(" " + mHdrClamperFlagState);
         pw.println(" " + mNbmControllerFlagState);
         pw.println(" " + mPowerThrottlingClamperFlagState);
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 9968ba5..e3ebe5b 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
@@ -92,6 +92,14 @@
 }
 
 flag {
+    name: "display_topology"
+    namespace: "display_manager"
+    description: "Display topology for moving cursors and windows between extended displays"
+    bug: "278199220"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "enable_displays_refresh_rates_synchronization"
     namespace: "display_manager"
     description: "Enables synchronization of refresh rates across displays"
diff --git a/services/core/java/com/android/server/flags/pinner.aconfig b/services/core/java/com/android/server/flags/pinner.aconfig
index 2f817db..3453668 100644
--- a/services/core/java/com/android/server/flags/pinner.aconfig
+++ b/services/core/java/com/android/server/flags/pinner.aconfig
@@ -9,8 +9,11 @@
 }
 
 flag {
-    name: "skip_home_art_pins"
-    namespace: "system_performance"
-    description: "Ablation study flag that controls if home app odex/vdex files should be pinned in memory."
-    bug: "340935152"
-}
\ No newline at end of file
+  name: "pin_global_quota"
+  namespace: "system_performance"
+  description: "This flag controls whether pinner will use a global quota or not"
+  bug: "340935152"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 5696fba..f2e2f65 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -1207,9 +1207,11 @@
 
         @Override
         public void onValues(int result, short addr) {
-            if (result == Result.SUCCESS) {
-                synchronized (mLock) {
-                    mPhysicalAddress = new Short(addr).intValue();
+            synchronized (mLock) {
+                if (result == Result.SUCCESS) {
+                    mPhysicalAddress = Short.toUnsignedInt(addr);
+                } else {
+                    mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
                 }
             }
         }
@@ -1605,9 +1607,11 @@
 
         @Override
         public void onValues(int result, short addr) {
-            if (result == Result.SUCCESS) {
-                synchronized (mLock) {
-                    mPhysicalAddress = new Short(addr).intValue();
+            synchronized (mLock) {
+                if (result == Result.SUCCESS) {
+                    mPhysicalAddress = Short.toUnsignedInt(addr);
+                } else {
+                    mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 101596d..aae7b59 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -261,6 +261,7 @@
                 .setDisplayName(HdmiUtils.getDefaultDeviceName(source))
                 .setDeviceType(deviceTypes.get(0))
                 .setVendorId(Constants.VENDOR_ID_UNKNOWN)
+                .setPortId(mService.getHdmiCecNetwork().physicalAddressToPortId(physicalAddress))
                 .build();
         mService.getHdmiCecNetwork().addCecDevice(newDevice);
     }
@@ -1433,6 +1434,7 @@
     protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
         assertRunOnServiceThread();
         mService.unregisterTvInputCallback(mTvInputCallback);
+        mTvInputs.clear();
         // Remove any repeated working actions.
         // HotplugDetectionAction will be reinstated during the wake up process.
         // HdmiControlService.onWakeUp() -> initializeLocalDevices() ->
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 73f18d1..99f7f12 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -23,6 +23,7 @@
 import android.hardware.display.DisplayViewport;
 import android.hardware.input.KeyGestureEvent;
 import android.os.IBinder;
+import android.util.SparseBooleanArray;
 import android.view.InputChannel;
 import android.view.inputmethod.InputMethodSubtype;
 
@@ -45,9 +46,11 @@
 
     /**
      * Called by the power manager to tell the input manager whether it should start
-     * watching for wake events.
+     * watching for wake events on given displays.
+     *
+     * @param displayInteractivities Map of display ids to their current interactive state.
      */
-    public abstract void setInteractive(boolean interactive);
+    public abstract void setDisplayInteractivities(SparseBooleanArray displayInteractivities);
 
     /**
      * Toggles Caps Lock state for input device with specific id.
@@ -232,6 +235,9 @@
     /**
      * Notify key gesture was completed by the user.
      *
+     * NOTE: This is a temporary API added to assist in a long-term refactor, and is not meant for
+     * general use by system services.
+     *
      * @param deviceId the device ID of the keyboard using which the event was completed
      * @param keycodes the keys pressed for the event
      * @param modifierState the modifier state
@@ -240,4 +246,20 @@
      */
     public abstract void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
             @KeyGestureEvent.KeyGestureType int event);
+
+    /**
+     * Notify that a key gesture was detected by another system component, and it should be handled
+     * appropriately by KeyGestureController.
+     *
+     * NOTE: This is a temporary API added to assist in a long-term refactor, and is not meant for
+     * general use by system services.
+     *
+     * @param deviceId the device ID of the keyboard using which the event was completed
+     * @param keycodes the keys pressed for the event
+     * @param modifierState the modifier state
+     * @param event the gesture event that was completed
+     *
+     */
+    public abstract void handleKeyGestureInKeyGestureController(int deviceId, int[] keycodes,
+            int modifierState, @KeyGestureEvent.KeyGestureType int event);
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 65adaba..f045576 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,6 +25,7 @@
 
 import static com.android.hardware.input.Flags.touchpadVisualizer;
 import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
 
 import android.Manifest;
 import android.annotation.EnforcePermission;
@@ -95,6 +96,7 @@
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
@@ -117,6 +119,7 @@
 import android.view.VerifiedInputEvent;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
@@ -325,6 +328,9 @@
     // Manages Sticky modifier state
     private final StickyModifierStateController mStickyModifierStateController;
     private final KeyGestureController mKeyGestureController;
+    /** Fallback actions by key code */
+    private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions =
+            new SparseArray<>();
 
     // Manages Keyboard microphone mute led
     private final KeyboardLedController mKeyboardLedController;
@@ -611,6 +617,7 @@
         mKeyRemapper.systemRunning();
         mPointerIconCache.systemRunning();
         mKeyboardGlyphManager.systemRunning();
+        mKeyGestureController.systemRunning();
 
         initKeyGestures();
     }
@@ -2469,6 +2476,14 @@
                 mFocusEventDebugView.reportKeyEvent(event);
             }
         }
+        if (useKeyGestureEventHandler() && mKeyGestureController.interceptKeyBeforeQueueing(event,
+                policyFlags)) {
+            // If key gesture gets triggered, we send the event to policy with KEY_GESTURE flag
+            // indicating, the event is used in triggering a key gesture. We can't block event
+            // like Power or volume keys since policy might still want to handle it to change
+            // certain states.
+            policyFlags |= WindowManagerPolicyConstants.FLAG_KEY_GESTURE_TRIGGERED;
+        }
         return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
     }
 
@@ -2511,6 +2526,74 @@
                 null, null, null) == PERMISSION_GRANTED;
     }
 
+    // Native callback.
+    @SuppressWarnings("unused")
+    private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
+        if (interceptUnhandledKey(event, focus)) {
+            return null;
+        }
+        // TODO(b/358569822): Move fallback logic to KeyGestureController
+        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
+            return null;
+        }
+        final KeyCharacterMap kcm = event.getKeyCharacterMap();
+        final int keyCode = event.getKeyCode();
+        final int metaState = event.getMetaState();
+        final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
+                && event.getRepeatCount() == 0;
+
+        // Check for fallback actions specified by the key character map.
+        final KeyCharacterMap.FallbackAction fallbackAction;
+        if (initialDown) {
+            fallbackAction = kcm.getFallbackAction(keyCode, metaState);
+        } else {
+            fallbackAction = mFallbackActions.get(keyCode);
+        }
+
+        if (fallbackAction == null) {
+            return null;
+        }
+        KeyEvent fallbackEvent = null;
+        final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
+        fallbackEvent = KeyEvent.obtain(
+                event.getDownTime(), event.getEventTime(),
+                event.getAction(), fallbackAction.keyCode,
+                event.getRepeatCount(), fallbackAction.metaState,
+                event.getDeviceId(), event.getScanCode(),
+                flags, event.getSource(), event.getDisplayId(), null);
+
+        if (!interceptFallback(focus, fallbackEvent, policyFlags)) {
+            fallbackEvent.recycle();
+            fallbackEvent = null;
+        }
+
+        if (initialDown) {
+            mFallbackActions.put(keyCode, fallbackAction);
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+            mFallbackActions.remove(keyCode);
+            fallbackAction.recycle();
+        }
+        return fallbackEvent;
+    }
+
+    private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
+            int policyFlags) {
+        int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
+        if ((actions & ACTION_PASS_TO_USER) == 0) {
+            return false;
+        }
+        long delayMillis = interceptKeyBeforeDispatching(focusedToken, fallbackEvent, policyFlags);
+        return delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken);
+    }
+
+    private boolean interceptUnhandledKey(KeyEvent event, IBinder focus) {
+        if (useKeyGestureEventHandler() && mKeyGestureController.interceptUnhandledKey(event,
+                focus)) {
+            return true;
+        }
+        return mWindowManagerCallbacks.interceptUnhandledKey(event, focus);
+    }
+
     private void initKeyGestures() {
         if (!useKeyGestureEventHandler()) {
             return;
@@ -2566,12 +2649,6 @@
 
     // Native callback.
     @SuppressWarnings("unused")
-    private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
-        return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
-    }
-
-    // Native callback.
-    @SuppressWarnings("unused")
     private void onPointerDownOutsideFocus(IBinder touchedToken) {
         mWindowManagerCallbacks.onPointerDownOutsideFocus(touchedToken);
     }
@@ -2986,9 +3063,9 @@
         long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags);
 
         /**
-         * Dispatch unhandled key
+         * Intercept unhandled key
          */
-        KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags);
+        boolean interceptUnhandledKey(KeyEvent event, IBinder token);
 
         int getPointerLayer();
 
@@ -3261,10 +3338,22 @@
         }
 
         @Override
-        public void setInteractive(boolean interactive) {
-            mNative.setInteractive(interactive);
-            mBatteryController.onInteractiveChanged(interactive);
-            mKeyboardBacklightController.onInteractiveChanged(interactive);
+        public void setDisplayInteractivities(SparseBooleanArray displayInteractivities) {
+            boolean globallyInteractive = false;
+            ArraySet<Integer> nonInteractiveDisplays = new ArraySet<>();
+            for (int i = 0; i < displayInteractivities.size(); i++) {
+                final int displayId = displayInteractivities.keyAt(i);
+                final boolean displayInteractive = displayInteractivities.get(displayId);
+                if (displayInteractive) {
+                    globallyInteractive = true;
+                } else {
+                    nonInteractiveDisplays.add(displayId);
+                }
+            }
+            mNative.setNonInteractiveDisplays(
+                    nonInteractiveDisplays.stream().mapToInt(Integer::intValue).toArray());
+            mBatteryController.onInteractiveChanged(globallyInteractive);
+            mKeyboardBacklightController.onInteractiveChanged(globallyInteractive);
         }
 
         // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture
@@ -3408,6 +3497,12 @@
             mKeyGestureController.notifyKeyGestureCompleted(deviceId, keycodes, modifierState,
                     gestureType);
         }
+
+        @Override
+        public void handleKeyGestureInKeyGestureController(int deviceId, int[] keycodes,
+                int modifierState, @KeyGestureEvent.KeyGestureType int gestureType) {
+            mKeyGestureController.handleKeyGesture(deviceId, keycodes, modifierState, gestureType);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 7fe7891..b488db5 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -16,15 +16,23 @@
 
 package com.android.server.input;
 
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE;
+
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
 
 import android.annotation.BinderThread;
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
-
+import android.database.ContentObserver;
 import android.hardware.input.AidlKeyGestureEvent;
 import android.hardware.input.IKeyGestureEventListener;
 import android.hardware.input.IKeyGestureHandler;
@@ -36,18 +44,23 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.InputDevice;
+import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.policy.KeyCombinationManager;
 
 import java.util.ArrayDeque;
 import java.util.HashSet;
@@ -83,20 +96,36 @@
     private static final int SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY = 1;
     private static final int LAST_SEARCH_KEY_BEHAVIOR = SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY;
 
+    // must match: config_keyChordPowerVolumeUp in config.xml
+    static final int POWER_VOLUME_UP_BEHAVIOR_NOTHING = 0;
+    static final int POWER_VOLUME_UP_BEHAVIOR_MUTE = 1;
+    static final int POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS = 2;
+
     private final Context mContext;
     private final Handler mHandler;
     private final int mSystemPid;
+    private final KeyCombinationManager mKeyCombinationManager;
+    private final SettingsObserver mSettingsObserver;
 
     // Pending actions
     private boolean mPendingMetaAction;
     private boolean mPendingCapsLockToggle;
     private boolean mPendingHideRecentSwitcher;
 
-    // Key behaviors
+    // Platform behaviors
     private boolean mEnableBugReportKeyboardShortcut;
+    private boolean mHasFeatureWatch;
+    private boolean mHasFeatureLeanback;
+
+    // Key behaviors
     private int mSearchKeyBehavior;
     private int mSettingsKeyBehavior;
 
+    // Settings behaviors
+    private int mRingerToggleChord = Settings.Secure.VOLUME_HUSH_OFF;
+    private int mPowerVolUpBehavior;
+
+
     // List of currently registered key gesture event listeners keyed by process pid
     @GuardedBy("mKeyGestureEventListenerRecords")
     private final SparseArray<KeyGestureEventListenerRecord>
@@ -129,12 +158,19 @@
                 return Integer.compare(p1, p2);
             }
         });
+        mKeyCombinationManager = new KeyCombinationManager(mHandler);
+        mSettingsObserver = new SettingsObserver(mHandler);
         initBehaviors();
+        initKeyCombinationRules();
     }
 
     private void initBehaviors() {
         mEnableBugReportKeyboardShortcut = "1".equals(SystemProperties.get("ro.debuggable"));
 
+        PackageManager pm = mContext.getPackageManager();
+        mHasFeatureWatch = pm.hasSystemFeature(FEATURE_WATCH);
+        mHasFeatureLeanback = pm.hasSystemFeature(FEATURE_LEANBACK);
+
         Resources res = mContext.getResources();
         mSearchKeyBehavior = res.getInteger(R.integer.config_searchKeyBehavior);
         if (mSearchKeyBehavior < SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH
@@ -146,6 +182,251 @@
                 || mSettingsKeyBehavior > LAST_SETTINGS_KEY_BEHAVIOR) {
             mSettingsKeyBehavior = SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY;
         }
+
+        mHandler.post(this::initBehaviorsFromSettings);
+    }
+
+    private void initBehaviorsFromSettings() {
+        ContentResolver resolver = mContext.getContentResolver();
+        mRingerToggleChord = Settings.Secure.getIntForUser(resolver,
+                Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.VOLUME_HUSH_OFF,
+                UserHandle.USER_CURRENT);
+
+        mPowerVolUpBehavior = Settings.Global.getInt(resolver,
+                Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+                mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_keyChordPowerVolumeUp));
+    }
+
+    private void initKeyCombinationRules() {
+        if (!useKeyGestureEventHandler() || !useKeyGestureEventHandlerMultiPressGestures()) {
+            return;
+        }
+        // TODO(b/358569822): Handle Power, Back key properly since key combination gesture is
+        //  captured here and rest of the Power, Back key behaviors are handled in PWM
+        final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enableScreenshotChord);
+
+        if (screenshotChordEnabled) {
+            mKeyCombinationManager.addRule(
+                    new KeyCombinationManager.TwoKeysCombinationRule(KeyEvent.KEYCODE_VOLUME_DOWN,
+                            KeyEvent.KEYCODE_POWER) {
+                        @Override
+                        public boolean preCondition() {
+                            return isKeyGestureSupported(
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
+                        }
+
+                        @Override
+                        public void execute() {
+                            handleKeyGesture(
+                                    new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER},
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
+                                    KeyGestureEvent.ACTION_GESTURE_START, 0);
+                        }
+
+                        @Override
+                        public void cancel() {
+                            handleKeyGesture(
+                                    new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER},
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
+                                    KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                    KeyGestureEvent.FLAG_CANCELLED);
+                        }
+                    });
+
+            if (mHasFeatureWatch) {
+                mKeyCombinationManager.addRule(
+                        new KeyCombinationManager.TwoKeysCombinationRule(KeyEvent.KEYCODE_POWER,
+                                KeyEvent.KEYCODE_STEM_PRIMARY) {
+                            @Override
+                            public boolean preCondition() {
+                                return isKeyGestureSupported(
+                                        KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
+                            }
+
+                            @Override
+                            public void execute() {
+                                handleKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
+                                                KeyEvent.KEYCODE_STEM_PRIMARY},
+                                        KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
+                                        KeyGestureEvent.ACTION_GESTURE_START, 0);
+                            }
+                            @Override
+                            public void cancel() {
+                                handleKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
+                                                KeyEvent.KEYCODE_STEM_PRIMARY},
+                                        KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
+                                        KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                        KeyGestureEvent.FLAG_CANCELLED);
+                            }
+                        });
+            }
+        }
+
+        mKeyCombinationManager.addRule(
+                new KeyCombinationManager.TwoKeysCombinationRule(KeyEvent.KEYCODE_VOLUME_DOWN,
+                        KeyEvent.KEYCODE_VOLUME_UP) {
+                    @Override
+                    public boolean preCondition() {
+                        return isKeyGestureSupported(
+                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD);
+                    }
+
+                    @Override
+                    public void execute() {
+                        handleKeyGesture(
+                                new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP},
+                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
+                                KeyGestureEvent.ACTION_GESTURE_START, 0);
+                    }
+
+                    @Override
+                    public void cancel() {
+                        handleKeyGesture(
+                                new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP},
+                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                KeyGestureEvent.FLAG_CANCELLED);
+                    }
+                });
+
+        // Volume up + power can either be the "ringer toggle chord" or as another way to
+        // launch GlobalActions. This behavior can change at runtime so we must check behavior
+        // inside the TwoKeysCombinationRule.
+        mKeyCombinationManager.addRule(
+                new KeyCombinationManager.TwoKeysCombinationRule(KeyEvent.KEYCODE_VOLUME_UP,
+                        KeyEvent.KEYCODE_POWER) {
+                    @Override
+                    public boolean preCondition() {
+                        if (!isKeyGestureSupported(getGestureType())) {
+                            return false;
+                        }
+                        switch (mPowerVolUpBehavior) {
+                            case POWER_VOLUME_UP_BEHAVIOR_MUTE:
+                                return mRingerToggleChord != Settings.Secure.VOLUME_HUSH_OFF;
+                            default:
+                                return true;
+                        }
+                    }
+                    @Override
+                    public void execute() {
+                        int gestureType = getGestureType();
+                        if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
+                            return;
+                        }
+                        handleKeyGesture(
+                                new int[]{KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_POWER},
+                                gestureType, KeyGestureEvent.ACTION_GESTURE_START, 0);
+                    }
+                    @Override
+                    public void cancel() {
+                        int gestureType = getGestureType();
+                        if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
+                            return;
+                        }
+                        handleKeyGesture(
+                                new int[]{KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_POWER},
+                                gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                KeyGestureEvent.FLAG_CANCELLED);
+                    }
+
+                    @KeyGestureEvent.KeyGestureType
+                    private int getGestureType() {
+                        switch (mPowerVolUpBehavior) {
+                            case POWER_VOLUME_UP_BEHAVIOR_MUTE -> {
+                                return KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD;
+                            }
+                            case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS -> {
+                                return KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS;
+                            }
+                            default -> {
+                                return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
+                            }
+                        }
+                    }
+                });
+
+        if (mHasFeatureLeanback) {
+            mKeyCombinationManager.addRule(
+                    new KeyCombinationManager.TwoKeysCombinationRule(KeyEvent.KEYCODE_BACK,
+                            KeyEvent.KEYCODE_DPAD_DOWN) {
+                        @Override
+                        public boolean preCondition() {
+                            return isKeyGestureSupported(
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD);
+                        }
+
+                        @Override
+                        public void execute() {
+                            handleKeyGesture(
+                                    new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
+                                    KeyGestureEvent.ACTION_GESTURE_START, 0);
+                        }
+
+                        @Override
+                        public void cancel() {
+                            handleKeyGesture(
+                                    new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
+                                    KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                    KeyGestureEvent.FLAG_CANCELLED);
+                        }
+                        @Override
+                        public long getKeyInterceptDelayMs() {
+                            // Use a timeout of 0 to prevent additional latency in processing of
+                            // this key. This will potentially cause some unwanted UI actions if the
+                            // user does end up triggering the key combination later, but in most
+                            // cases, the user will simply hit a single key, and this will allow us
+                            // to process it without first waiting to see if the combination is
+                            // going to be triggered.
+                            return 0;
+                        }
+                    });
+
+            mKeyCombinationManager.addRule(
+                    new KeyCombinationManager.TwoKeysCombinationRule(KeyEvent.KEYCODE_BACK,
+                            KeyEvent.KEYCODE_DPAD_CENTER) {
+                        @Override
+                        public boolean preCondition() {
+                            return isKeyGestureSupported(
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT);
+                        }
+
+                        @Override
+                        public void execute() {
+                            handleKeyGesture(
+                                    new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER},
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
+                                    KeyGestureEvent.ACTION_GESTURE_START, 0);
+                        }
+                        @Override
+                        public void cancel() {
+                            handleKeyGesture(
+                                    new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER},
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
+                                    KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                    KeyGestureEvent.FLAG_CANCELLED);
+                        }
+                        @Override
+                        public long getKeyInterceptDelayMs() {
+                            return 0;
+                        }
+                    });
+        }
+    }
+
+    public void systemRunning() {
+        mSettingsObserver.observe();
+    }
+
+    public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
+        final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
+        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+            return mKeyCombinationManager.interceptKey(event, interactive);
+        }
+        return false;
     }
 
     public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
@@ -154,9 +435,22 @@
         //  KeyGestureHandler (PWM is one of the handlers)
         final int keyCode = event.getKeyCode();
         final int deviceId = event.getDeviceId();
+        final int flags = event.getFlags();
         final long keyConsumed = -1;
         final long keyNotConsumed = 0;
 
+        if (mKeyCombinationManager.isKeyConsumed(event)) {
+            return keyConsumed;
+        }
+
+        if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
+            final long now = SystemClock.uptimeMillis();
+            final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode);
+            if (now < interceptTimeout) {
+                return interceptTimeout - now;
+            }
+        }
+
         Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
         if (consumedKeys == null) {
             consumedKeys = new HashSet<>();
@@ -578,12 +872,76 @@
         return false;
     }
 
+    boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
+        final int keyCode = event.getKeyCode();
+        final int repeatCount = event.getRepeatCount();
+        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+        final int metaState = event.getModifiers();
+        final int deviceId = event.getDeviceId();
+        final int displayId = event.getDisplayId();
+
+        switch(keyCode) {
+            case KeyEvent.KEYCODE_SPACE:
+                if (down && repeatCount == 0) {
+                    // Handle keyboard layout switching. (CTRL + SPACE)
+                    if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK,
+                            KeyEvent.META_CTRL_ON)) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_CTRL_ON | (event.isShiftPressed()
+                                        ? KeyEvent.META_SHIFT_ON : 0),
+                                KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                                focusedToken, /* flags = */0);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_Z:
+                if (down && KeyEvent.metaStateHasModifiers(metaState,
+                        KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) {
+                    // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
+                    return handleKeyGesture(deviceId, new int[]{keyCode},
+                            KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_SYSRQ:
+                if (down && repeatCount == 0) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+            case KeyEvent.KEYCODE_ESCAPE:
+                if (down && KeyEvent.metaStateHasNoModifiers(metaState) && repeatCount == 0) {
+                    return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+                            focusedToken, /* flags = */0);
+                }
+                break;
+        }
+
+        return false;
+    }
+
+    private boolean handleKeyGesture(int[] keycodes,
+            @KeyGestureEvent.KeyGestureType int gestureType, int action, int flags) {
+        return handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, keycodes, /* modifierState= */0,
+                gestureType, action, Display.DEFAULT_DISPLAY, /* focusedToken = */null, flags);
+    }
+
     @VisibleForTesting
     boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
             @KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId,
-            IBinder focusedToken, int flags) {
-        AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes,
-                modifierState, gestureType, action, displayId, flags);
+            @Nullable IBinder focusedToken, int flags) {
+        return handleKeyGesture(createKeyGestureEvent(deviceId, keycodes,
+                modifierState, gestureType, action, displayId, flags), focusedToken);
+    }
+
+    private boolean handleKeyGesture(AidlKeyGestureEvent event, @Nullable IBinder focusedToken) {
         synchronized (mKeyGestureHandlerRecords) {
             for (KeyGestureHandlerRecord handler : mKeyGestureHandlerRecords.values()) {
                 if (handler.handleKeyGesture(event, focusedToken)) {
@@ -616,10 +974,17 @@
         mHandler.obtainMessage(MSG_NOTIFY_KEY_GESTURE_EVENT, event).sendToTarget();
     }
 
+    public void handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
+            @KeyGestureEvent.KeyGestureType int gestureType) {
+        AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes, modifierState,
+                gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY, 0);
+        handleKeyGesture(event, null /*focusedToken*/);
+    }
+
     @MainThread
     private void notifyKeyGestureEvent(AidlKeyGestureEvent event) {
         InputDevice device = getInputDevice(event.deviceId);
-        if (device == null || device.isVirtual()) {
+        if (device == null) {
             return;
         }
         if (event.action == KeyGestureEvent.ACTION_GESTURE_COMPLETE) {
@@ -813,6 +1178,27 @@
         }
     }
 
+    private class SettingsObserver extends ContentObserver {
+        private SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        private void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                            Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Global.getUriFor(
+                            Settings.Global.KEY_CHORD_POWER_VOLUME_UP), false, this,
+                    UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            initBehaviorsFromSettings();
+        }
+    }
+
     @Nullable
     private InputDevice getInputDevice(int deviceId) {
         InputManager inputManager = mContext.getSystemService(InputManager.class);
@@ -842,6 +1228,8 @@
         ipw.println("mPendingHideRecentSwitcher = " + mPendingHideRecentSwitcher);
         ipw.println("mSearchKeyBehavior = " + mSearchKeyBehavior);
         ipw.println("mSettingsKeyBehavior = " + mSettingsKeyBehavior);
+        ipw.println("mRingerToggleChord = " + mRingerToggleChord);
+        ipw.println("mPowerVolUpBehavior = " + mPowerVolUpBehavior);
         ipw.print("mKeyGestureEventListenerRecords = {");
         synchronized (mKeyGestureEventListenerRecords) {
             int size = mKeyGestureEventListenerRecords.size();
@@ -872,5 +1260,6 @@
             ipw.println(ev);
         }
         ipw.decreaseIndent();
+        mKeyCombinationManager.dump("", ipw);
     }
 }
diff --git a/services/core/java/com/android/server/input/KeyRemapper.java b/services/core/java/com/android/server/input/KeyRemapper.java
index 7ba7769..82b36af 100644
--- a/services/core/java/com/android/server/input/KeyRemapper.java
+++ b/services/core/java/com/android/server/input/KeyRemapper.java
@@ -17,27 +17,24 @@
 package com.android.server.input;
 
 import android.content.Context;
-import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.util.ArrayMap;
 import android.util.FeatureFlagUtils;
-import android.view.InputDevice;
 
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.Map;
-import java.util.Objects;
 
 /**
  * A component of {@link InputManagerService} responsible for managing key remappings.
  *
  * @hide
  */
-final class KeyRemapper implements InputManager.InputDeviceListener {
+final class KeyRemapper {
 
-    private static final int MSG_UPDATE_EXISTING_DEVICES = 1;
+    private static final int MSG_UPDATE_EXISTING_KEY_REMAPPING = 1;
     private static final int MSG_REMAP_KEY = 2;
     private static final int MSG_CLEAR_ALL_REMAPPING = 3;
 
@@ -49,7 +46,7 @@
     private final Handler mHandler;
 
     KeyRemapper(Context context, NativeInputManagerService nativeService,
-            PersistentDataStore dataStore, Looper looper) {
+                PersistentDataStore dataStore, Looper looper) {
         mContext = context;
         mNative = nativeService;
         mDataStore = dataStore;
@@ -57,13 +54,7 @@
     }
 
     public void systemRunning() {
-        InputManager inputManager = Objects.requireNonNull(
-                mContext.getSystemService(InputManager.class));
-        inputManager.registerInputDeviceListener(this, mHandler);
-
-        Message msg = Message.obtain(mHandler, MSG_UPDATE_EXISTING_DEVICES,
-                inputManager.getInputDeviceIds());
-        mHandler.sendMessage(msg);
+        Message.obtain(mHandler, MSG_UPDATE_EXISTING_KEY_REMAPPING).sendToTarget();
     }
 
     public void remapKey(int fromKey, int toKey) {
@@ -91,19 +82,19 @@
         }
     }
 
-    private void addKeyRemapping(int fromKey, int toKey) {
-        InputManager inputManager = Objects.requireNonNull(
-                mContext.getSystemService(InputManager.class));
-        for (int deviceId : inputManager.getInputDeviceIds()) {
-            InputDevice inputDevice = inputManager.getInputDevice(deviceId);
-            if (inputDevice != null && !inputDevice.isVirtual() && inputDevice.isFullKeyboard()) {
-                mNative.addKeyRemapping(deviceId, fromKey, toKey);
-            }
+    private void setKeyRemapping(Map<Integer, Integer> keyRemapping) {
+        int index = 0;
+        int[] fromKeycodesArr = new int[keyRemapping.size()];
+        int[] toKeycodesArr = new int[keyRemapping.size()];
+        for (Map.Entry<Integer, Integer> entry : keyRemapping.entrySet()) {
+            fromKeycodesArr[index] = entry.getKey();
+            toKeycodesArr[index] = entry.getValue();
+            index++;
         }
+        mNative.setKeyRemapping(fromKeycodesArr, toKeycodesArr);
     }
 
     private void remapKeyInternal(int fromKey, int toKey) {
-        addKeyRemapping(fromKey, toKey);
         synchronized (mDataStore) {
             try {
                 if (fromKey == toKey) {
@@ -114,6 +105,7 @@
             } finally {
                 mDataStore.saveIfNeeded();
             }
+            setKeyRemapping(mDataStore.getKeyRemapping());
         }
     }
 
@@ -123,45 +115,25 @@
                 Map<Integer, Integer> keyRemapping = mDataStore.getKeyRemapping();
                 for (int fromKey : keyRemapping.keySet()) {
                     mDataStore.clearMappedKey(fromKey);
-
-                    // Remapping to itself will clear the remapping on native side
-                    addKeyRemapping(fromKey, fromKey);
                 }
             } finally {
                 mDataStore.saveIfNeeded();
             }
+            setKeyRemapping(mDataStore.getKeyRemapping());
         }
     }
 
-    @Override
-    public void onInputDeviceAdded(int deviceId) {
+    public void updateExistingKeyMapping() {
         if (!supportRemapping()) {
             return;
         }
-        InputManager inputManager = Objects.requireNonNull(
-                mContext.getSystemService(InputManager.class));
-        InputDevice inputDevice = inputManager.getInputDevice(deviceId);
-        if (inputDevice != null && !inputDevice.isVirtual() && inputDevice.isFullKeyboard()) {
-            Map<Integer, Integer> remapping = getKeyRemapping();
-            remapping.forEach(
-                    (fromKey, toKey) -> mNative.addKeyRemapping(deviceId, fromKey, toKey));
-        }
-    }
-
-    @Override
-    public void onInputDeviceRemoved(int deviceId) {
-    }
-
-    @Override
-    public void onInputDeviceChanged(int deviceId) {
+        setKeyRemapping(getKeyRemapping());
     }
 
     private boolean handleMessage(Message msg) {
         switch (msg.what) {
-            case MSG_UPDATE_EXISTING_DEVICES:
-                for (int deviceId : (int[]) msg.obj) {
-                    onInputDeviceAdded(deviceId);
-                }
+            case MSG_UPDATE_EXISTING_KEY_REMAPPING:
+                updateExistingKeyMapping();
                 return true;
             case MSG_REMAP_KEY:
                 remapKeyInternal(msg.arg1, msg.arg2);
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 5dd461d..4404d63 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -48,7 +48,7 @@
 
     int getSwitchState(int deviceId, int sourceMask, int sw);
 
-    void addKeyRemapping(int deviceId, int fromKeyCode, int toKeyCode);
+    void setKeyRemapping(int[] fromKeyCodes, int[] toKeyCodes);
 
     boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
 
@@ -141,7 +141,7 @@
 
     void setShowTouches(boolean enabled);
 
-    void setInteractive(boolean interactive);
+    void setNonInteractiveDisplays(int[] displayIds);
 
     void reloadCalibration();
 
@@ -311,7 +311,7 @@
         public native int getSwitchState(int deviceId, int sourceMask, int sw);
 
         @Override
-        public native void addKeyRemapping(int deviceId, int fromKeyCode, int toKeyCode);
+        public native void setKeyRemapping(int[] fromKeyCodes, int[] toKeyCodes);
 
         @Override
         public native boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes,
@@ -409,7 +409,7 @@
         public native void setShowTouches(boolean enabled);
 
         @Override
-        public native void setInteractive(boolean interactive);
+        public native void setNonInteractiveDisplays(int[] displayIds);
 
         @Override
         public native void reloadCalibration();
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
index 0e940d2..cf0c5b0 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
@@ -24,7 +24,6 @@
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.hardware.input.InputManager;
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.Gravity;
@@ -42,6 +41,7 @@
 import com.android.server.input.TouchpadHardwareState;
 
 import java.util.Objects;
+import java.util.function.Consumer;
 
 public class TouchpadDebugView extends LinearLayout {
     private static final float MAX_SCREEN_WIDTH_PROPORTION = 0.4f;
@@ -52,7 +52,8 @@
     private static final float DEFAULT_RES_Y = 45f;
     private static final int TEXT_PADDING_DP = 12;
     private static final int ROUNDED_CORNER_RADIUS_DP = 24;
-
+    private static final int BUTTON_PRESSED_BACKGROUND_COLOR = Color.rgb(118, 151, 99);
+    private static final int BUTTON_RELEASED_BACKGROUND_COLOR = Color.rgb(84, 85, 169);
     /**
      * Input device ID for the touchpad that this debug view is displaying.
      */
@@ -74,22 +75,24 @@
     private int mWindowLocationBeforeDragX;
     private int mWindowLocationBeforeDragY;
     private int mLatestGestureType = 0;
+    private TouchpadSelectionView mTouchpadSelectionView;
+    private TouchpadVisualizationView mTouchpadVisualizationView;
     private TextView mGestureInfoView;
     @NonNull
     private TouchpadHardwareState mLastTouchpadState =
             new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
                     new TouchpadFingerState[0]);
-    private TouchpadVisualizationView mTouchpadVisualizationView;
     private final TouchpadHardwareProperties mTouchpadHardwareProperties;
 
     public TouchpadDebugView(Context context, int touchpadId,
-                             TouchpadHardwareProperties touchpadHardwareProperties) {
+                             TouchpadHardwareProperties touchpadHardwareProperties,
+                             Consumer<Integer> touchpadSwitchHandler) {
         super(context);
         mTouchpadId = touchpadId;
         mWindowManager =
                 Objects.requireNonNull(getContext().getSystemService(WindowManager.class));
         mTouchpadHardwareProperties = touchpadHardwareProperties;
-        init(context, touchpadId);
+        init(context, touchpadId, touchpadSwitchHandler);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
 
         mWindowLayoutParams = new WindowManager.LayoutParams();
@@ -111,7 +114,8 @@
         mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
     }
 
-    private void init(Context context, int touchpadId) {
+    private void init(Context context, int touchpadId,
+                      Consumer<Integer> touchpadSwitchHandler) {
         updateScreenDimensions();
         setOrientation(VERTICAL);
         setLayoutParams(new LayoutParams(
@@ -119,35 +123,31 @@
                 LayoutParams.WRAP_CONTENT));
         setBackgroundColor(Color.TRANSPARENT);
 
-        TextView nameView = new TextView(context);
-        nameView.setBackgroundColor(Color.RED);
-        nameView.setTextSize(TEXT_SIZE_SP);
-        nameView.setText(Objects.requireNonNull(Objects.requireNonNull(
-                        mContext.getSystemService(InputManager.class))
-                .getInputDevice(touchpadId)).getName());
-        nameView.setGravity(Gravity.CENTER);
-        nameView.setTextColor(Color.WHITE);
+        mTouchpadSelectionView = new TouchpadSelectionView(context,
+                touchpadId, touchpadSwitchHandler);
+        mTouchpadSelectionView.setBackgroundColor(BUTTON_RELEASED_BACKGROUND_COLOR);
+        mTouchpadSelectionView.setGravity(Gravity.CENTER);
         int paddingInDP = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, TEXT_PADDING_DP,
                 getResources().getDisplayMetrics());
-        nameView.setPadding(paddingInDP, paddingInDP, paddingInDP, paddingInDP);
-        nameView.setLayoutParams(
+        mTouchpadSelectionView.setPadding(paddingInDP, paddingInDP, paddingInDP, paddingInDP);
+        mTouchpadSelectionView.setLayoutParams(
                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
 
         mTouchpadVisualizationView = new TouchpadVisualizationView(context,
                 mTouchpadHardwareProperties);
-        mTouchpadVisualizationView.setBackgroundColor(Color.WHITE);
 
         mGestureInfoView = new TextView(context);
-        mGestureInfoView.setBackgroundColor(Color.BLACK);
         mGestureInfoView.setTextSize(TEXT_SIZE_SP);
         mGestureInfoView.setText("Latest Gesture: ");
         mGestureInfoView.setGravity(Gravity.CENTER);
-        mGestureInfoView.setTextColor(Color.WHITE);
         mGestureInfoView.setPadding(paddingInDP, paddingInDP, paddingInDP, paddingInDP);
         mGestureInfoView.setLayoutParams(
                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+        //TODO(b/369061237): Handle longer text
 
-        addView(nameView);
+        updateTheme(getResources().getConfiguration().uiMode);
+
+        addView(mTouchpadSelectionView);
         addView(mTouchpadVisualizationView);
         addView(mGestureInfoView);
 
@@ -180,7 +180,8 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (event.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE) {
+        if (event.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE
+                || event.getClassification() == MotionEvent.CLASSIFICATION_PINCH) {
             return false;
         }
 
@@ -239,6 +240,8 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+
+        updateTheme(newConfig.uiMode);
         updateScreenDimensions();
         updateViewsDimensions();
 
@@ -250,6 +253,27 @@
         mWindowManager.updateViewLayout(this, mWindowLayoutParams);
     }
 
+    private void updateTheme(int uiMode) {
+        int currentNightMode = uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        if (currentNightMode == Configuration.UI_MODE_NIGHT_YES) {
+            setNightModeTheme();
+        } else {
+            setLightModeTheme();
+        }
+    }
+
+    private void setLightModeTheme() {
+        mTouchpadVisualizationView.setLightModeTheme();
+        mGestureInfoView.setBackgroundColor(Color.WHITE);
+        mGestureInfoView.setTextColor(Color.BLACK);
+    }
+
+    private void setNightModeTheme() {
+        mTouchpadVisualizationView.setNightModeTheme();
+        mGestureInfoView.setBackgroundColor(Color.BLACK);
+        mGestureInfoView.setTextColor(Color.WHITE);
+    }
+
     private boolean isSlopExceeded(float deltaX, float deltaY) {
         return deltaX * deltaX + deltaY * deltaY >= mTouchSlop * mTouchSlop;
     }
@@ -333,12 +357,12 @@
 
     private void onTouchpadButtonPress() {
         Slog.d(TAG, "You clicked me!");
-        getChildAt(0).setBackgroundColor(Color.BLUE);
+        mTouchpadSelectionView.setBackgroundColor(BUTTON_PRESSED_BACKGROUND_COLOR);
     }
 
     private void onTouchpadButtonRelease() {
         Slog.d(TAG, "You released the click");
-        getChildAt(0).setBackgroundColor(Color.RED);
+        mTouchpadSelectionView.setBackgroundColor(BUTTON_RELEASED_BACKGROUND_COLOR);
     }
 
     /**
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
index cb43977..9cfbfa64 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.input.debug;
 
+import android.annotation.AnyThread;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.input.InputManager;
@@ -45,8 +46,8 @@
     private boolean mTouchpadVisualizerEnabled = false;
 
     public TouchpadDebugViewController(Context context, Looper looper,
-            InputManagerService inputManagerService) {
-        //TODO(b/363979581): Handle multi-display scenarios
+                                       InputManagerService inputManagerService) {
+        //TODO(b/369059937): Handle multi-display scenarios
         mContext = context;
         mHandler = new Handler(looper);
         mInputManagerService = inputManagerService;
@@ -77,6 +78,14 @@
         }
     }
 
+    /**
+     * Switch to showing the touchpad with the given device ID
+     */
+    public void switchVisualisationToTouchpadId(int newDeviceId) {
+        if (mTouchpadDebugView != null) hideDebugView(mTouchpadDebugView.getTouchpadId());
+        showDebugView(newDeviceId);
+    }
+
     @Override
     public void onInputDeviceChanged(int deviceId) {
     }
@@ -117,7 +126,7 @@
                         touchpadId);
 
         mTouchpadDebugView = new TouchpadDebugView(mContext, touchpadId,
-                touchpadHardwareProperties);
+                touchpadHardwareProperties, this::switchVisualisationToTouchpadId);
         final WindowManager.LayoutParams mWindowLayoutParams =
                 mTouchpadDebugView.getWindowLayoutParams();
 
@@ -149,19 +158,28 @@
      * @param touchpadHardwareState the hardware state of a touchpad
      * @param deviceId              the deviceId of the touchpad that is sending the hardware state
      */
+    @AnyThread
     public void updateTouchpadHardwareState(TouchpadHardwareState touchpadHardwareState,
-            int deviceId) {
-        if (mTouchpadDebugView != null) {
-            mTouchpadDebugView.updateHardwareState(touchpadHardwareState, deviceId);
-        }
+                                            int deviceId) {
+        mHandler.post(() -> {
+            if (mTouchpadDebugView != null) {
+                mTouchpadDebugView.post(
+                        () -> mTouchpadDebugView.updateHardwareState(touchpadHardwareState,
+                                deviceId));
+            }
+        });
     }
 
     /**
      * Notify the TouchpadDebugView of a new touchpad gesture.
      */
+    @AnyThread
     public void updateTouchpadGestureInfo(int gestureType, int deviceId) {
-        if (mTouchpadDebugView != null) {
-            mTouchpadDebugView.updateGestureInfo(gestureType, deviceId);
-        }
+        mHandler.post(() -> {
+            if (mTouchpadDebugView != null) {
+                mTouchpadDebugView.post(
+                        () -> mTouchpadDebugView.updateGestureInfo(gestureType, deviceId));
+            }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/input/debug/TouchpadSelectionView.java b/services/core/java/com/android/server/input/debug/TouchpadSelectionView.java
new file mode 100644
index 0000000..05217b6
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/TouchpadSelectionView.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package com.android.server.input.debug;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.hardware.input.InputManager;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupMenu;
+import android.widget.TextView;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+public class TouchpadSelectionView extends LinearLayout {
+    private static final float TEXT_SIZE_SP = 16.0f;
+
+    int mCurrentTouchpadId;
+
+    public TouchpadSelectionView(Context context, int touchpadId,
+                                 Consumer<Integer> touchpadSwitchHandler) {
+        super(context);
+        mCurrentTouchpadId = touchpadId;
+        init(context, touchpadSwitchHandler);
+    }
+
+    private void init(Context context, Consumer<Integer> touchpadSwitchHandler) {
+        setOrientation(HORIZONTAL);
+        setLayoutParams(new LayoutParams(
+                LayoutParams.WRAP_CONTENT,
+                LayoutParams.WRAP_CONTENT));
+        setBackgroundColor(Color.TRANSPARENT);
+
+        TextView nameView = new TextView(context);
+        nameView.setTextSize(TEXT_SIZE_SP);
+        nameView.setText(getTouchpadName(mCurrentTouchpadId));
+        nameView.setGravity(Gravity.LEFT);
+        nameView.setTextColor(Color.WHITE);
+
+        LayoutParams textParams = new LayoutParams(
+                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+        textParams.rightMargin = 16;
+        nameView.setLayoutParams(textParams);
+
+        ImageButton arrowButton = new ImageButton(context);
+        arrowButton.setImageDrawable(context.getDrawable(android.R.drawable.arrow_down_float));
+        arrowButton.setForegroundGravity(Gravity.RIGHT);
+        arrowButton.setBackgroundColor(Color.TRANSPARENT);
+        arrowButton.setLayoutParams(new LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
+
+        arrowButton.setOnClickListener(v -> showPopupMenu(v, context, touchpadSwitchHandler));
+
+        addView(nameView);
+        addView(arrowButton);
+    }
+
+    private void showPopupMenu(View anchorView, Context context,
+                               Consumer<Integer> touchpadSwitchHandler) {
+        int i = 0;
+        PopupMenu popupMenu = new PopupMenu(context, anchorView);
+
+        final InputManager inputManager = Objects.requireNonNull(
+                mContext.getSystemService(InputManager.class));
+        for (int deviceId : inputManager.getInputDeviceIds()) {
+            InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+            if (Objects.requireNonNull(inputDevice).supportsSource(
+                    InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)) {
+                popupMenu.getMenu().add(0, deviceId, i, getTouchpadName(deviceId));
+                i++;
+            }
+        }
+
+        popupMenu.setOnMenuItemClickListener(item -> {
+            if (item.getItemId() == mCurrentTouchpadId) {
+                return false;
+            }
+
+            touchpadSwitchHandler.accept(item.getItemId());
+            return true;
+        });
+
+        popupMenu.show();
+    }
+
+    private String getTouchpadName(int touchpadId) {
+        return Objects.requireNonNull(Objects.requireNonNull(
+                        mContext.getSystemService(InputManager.class))
+                .getInputDevice(touchpadId)).getName();
+    }
+}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadVisualizationView.java b/services/core/java/com/android/server/input/debug/TouchpadVisualizationView.java
index 2eed9ba..eeec5cc 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadVisualizationView.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadVisualizationView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.RectF;
 import android.util.Slog;
@@ -29,6 +30,7 @@
 
 import java.util.ArrayDeque;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 public class TouchpadVisualizationView extends View {
@@ -49,6 +51,7 @@
     private final Paint mOvalFillPaint;
     private final Paint mTracePaint;
     private final Paint mCenterPointPaint;
+    private final Paint mPressureTextPaint;
     private final RectF mTempOvalRect = new RectF();
 
     public TouchpadVisualizationView(Context context,
@@ -58,11 +61,9 @@
         mScaleFactor = 1;
         mOvalStrokePaint = new Paint();
         mOvalStrokePaint.setAntiAlias(true);
-        mOvalStrokePaint.setARGB(255, 0, 0, 0);
         mOvalStrokePaint.setStyle(Paint.Style.STROKE);
         mOvalFillPaint = new Paint();
         mOvalFillPaint.setAntiAlias(true);
-        mOvalFillPaint.setARGB(255, 0, 0, 0);
         mTracePaint = new Paint();
         mTracePaint.setAntiAlias(false);
         mTracePaint.setARGB(255, 0, 0, 255);
@@ -72,6 +73,8 @@
         mCenterPointPaint.setAntiAlias(true);
         mCenterPointPaint.setARGB(255, 255, 0, 0);
         mCenterPointPaint.setStrokeWidth(2);
+        mPressureTextPaint = new Paint();
+        mPressureTextPaint.setAntiAlias(true);
     }
 
     private void removeOldPoints() {
@@ -135,6 +138,13 @@
             mOvalFillPaint.setAlpha((int) pressureToOpacity);
 
             drawOval(canvas, newX, newY, newTouchMajor, newTouchMinor, newAngle);
+
+            String formattedPressure = String.format(Locale.getDefault(), "Ps: %.2f",
+                    touchpadFingerState.getPressure());
+            float textWidth = mPressureTextPaint.measureText(formattedPressure);
+
+            canvas.drawText(formattedPressure, newX - textWidth / 2,
+                    newY - newTouchMajor / 2, mPressureTextPaint);
         }
 
         mTempFingerStatesByTrackingId.clear();
@@ -195,6 +205,26 @@
         mScaleFactor = scaleFactor;
     }
 
+    /**
+     * Change the colors of the objects inside the view to light mode theme.
+     */
+    public void setLightModeTheme() {
+        this.setBackgroundColor(Color.rgb(20, 20, 20));
+        mPressureTextPaint.setARGB(255, 255, 255, 255);
+        mOvalFillPaint.setARGB(255, 255, 255, 255);
+        mOvalStrokePaint.setARGB(255, 255, 255, 255);
+    }
+
+    /**
+     * Change the colors of the objects inside the view to night mode theme.
+     */
+    public void setNightModeTheme() {
+        this.setBackgroundColor(Color.rgb(240, 240, 240));
+        mPressureTextPaint.setARGB(255, 0, 0, 0);
+        mOvalFillPaint.setARGB(255, 0, 0, 0);
+        mOvalStrokePaint.setARGB(255, 0, 0, 0);
+    }
+
     private float translateX(float x) {
         return translateRange(mTouchpadHardwareProperties.getLeft(),
                 mTouchpadHardwareProperties.getRight(), 0, getWidth(), x);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2bb2b7b..f0fb33e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4799,7 +4799,8 @@
                 userData.mImeBindingState.mFocusedWindowEditorInfo,
                 info.focusedWindowName, userData.mImeBindingState.mFocusedWindowSoftInputMode,
                 reason, userData.mInFullscreenMode, info.requestWindowName,
-                info.imeControlTargetName, info.imeLayerTargetName, info.imeSurfaceParentName));
+                info.imeControlTargetName, info.imeLayerTargetName, info.imeSurfaceParentName,
+                userId));
 
         if (statsToken != null) {
             mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName);
@@ -6132,8 +6133,7 @@
             dumpAsStringNoCheckForUser(userData, fd, pw, args, isCritical);
         }
 
-        // TODO(b/365868861): Make StartInputHistory, SoftInputShowHideHistory and ImeTracker per
-        //  user.
+        // TODO(b/365868861): Make StartInputHistory and ImeTracker multi-user aware.
         synchronized (ImfLock.class) {
             p.println("  mStartInputHistory:");
             mStartInputHistory.dump(pw, "    ");
diff --git a/services/core/java/com/android/server/inputmethod/SoftInputShowHideHistory.java b/services/core/java/com/android/server/inputmethod/SoftInputShowHideHistory.java
index 3023603..8445632 100644
--- a/services/core/java/com/android/server/inputmethod/SoftInputShowHideHistory.java
+++ b/services/core/java/com/android/server/inputmethod/SoftInputShowHideHistory.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.SystemClock;
 import android.view.WindowManager;
 import android.view.inputmethod.EditorInfo;
@@ -62,6 +63,8 @@
         final String mImeTargetNameFromWm;
         @Nullable
         final String mImeSurfaceParentName;
+        @UserIdInt
+        final int mImeUserId;
 
         Entry(ClientState client, EditorInfo editorInfo,
                 String focusedWindowName,
@@ -69,7 +72,7 @@
                 @SoftInputShowHideReason int reason,
                 boolean inFullscreenMode, String requestWindowName,
                 @Nullable String imeControlTargetName, @Nullable String imeTargetName,
-                @Nullable String imeSurfaceParentName) {
+                @Nullable String imeSurfaceParentName, @UserIdInt int imeUserId) {
             mClientState = client;
             mEditorInfo = editorInfo;
             mFocusedWindowName = focusedWindowName;
@@ -82,6 +85,7 @@
             mImeControlTargetName = imeControlTargetName;
             mImeTargetNameFromWm = imeTargetName;
             mImeSurfaceParentName = imeSurfaceParentName;
+            mImeUserId = imeUserId;
         }
     }
 
@@ -102,7 +106,8 @@
                 continue;
             }
             pw.print(prefix);
-            pw.println("SoftInputShowHide #" + entry.mSequenceNumber + ":");
+            pw.println("SoftInputShowHide[" + entry.mImeUserId + "] #"
+                    + entry.mSequenceNumber + ":");
 
             pw.print(prefix);
             pw.println("  time=" + formatter.format(Instant.ofEpochMilli(entry.mWallTime))
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 48d24f2..47f579d 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -195,10 +195,9 @@
                     == PackageManager.PERMISSION_GRANTED) {
                 return true;
             }
-            boolean operationActive = mAppOps.isOperationActive(AppOpsManager.OP_PROJECT_MEDIA,
-                    mProjectionGrant.uid,
-                    mProjectionGrant.packageName);
-            if (operationActive) {
+            if (AppOpsManager.MODE_ALLOWED == mAppOps.noteOpNoThrow(AppOpsManager.OP_PROJECT_MEDIA,
+                    mProjectionGrant.uid, mProjectionGrant.packageName, /* attributionTag= */ null,
+                    "recording lockscreen")) {
                 // Some tools use media projection by granting the OP_PROJECT_MEDIA app
                 // op via a shell command. Those tools can be granted keyguard capture
                 return true;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ba7d4d2..79633f1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -33,6 +33,7 @@
 import static android.app.Notification.EXTRA_SUB_TEXT;
 import static android.app.Notification.EXTRA_TEXT;
 import static android.app.Notification.EXTRA_TEXT_LINES;
+import static android.app.Notification.EXTRA_TITLE;
 import static android.app.Notification.EXTRA_TITLE_BIG;
 import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
 import static android.app.Notification.FLAG_AUTO_CANCEL;
@@ -45,6 +46,7 @@
 import static android.app.Notification.FLAG_NO_DISMISS;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
+import static android.app.Notification.FLAG_PROMOTED_ONGOING;
 import static android.app.Notification.FLAG_USER_INITIATED_JOB;
 import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
 import static android.app.NotificationChannel.NEWS_ID;
@@ -2577,6 +2579,7 @@
                 mNotificationChannelLogger,
                 mAppOps,
                 mUserProfiles,
+                mUgmInternal,
                 mShowReviewPermissionsNotification,
                 Clock.systemUTC());
         mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper,
@@ -3516,7 +3519,7 @@
     private String getHistoryTitle(Notification n) {
         CharSequence title = null;
         if (n.extras != null) {
-            title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
+            title = n.extras.getCharSequence(EXTRA_TITLE);
             if (title == null) {
                 title = n.extras.getCharSequence(EXTRA_TITLE_BIG);
             }
@@ -4114,6 +4117,90 @@
         }
 
         @Override
+        @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+        public boolean appCanBePromoted(String pkg, int uid) {
+            checkCallerIsSystemOrSystemUiOrShell();
+            if (!android.app.Flags.apiRichOngoing()) {
+                return false;
+            }
+            return mPreferencesHelper.canBePromoted(pkg, uid);
+        }
+
+        @Override
+        @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+        public boolean canBePromoted(String callingPkg) {
+            checkCallerIsSameApp(callingPkg);
+            if (!android.app.Flags.apiRichOngoing()) {
+                return false;
+            }
+            return mPreferencesHelper.canBePromoted(callingPkg, Binder.getCallingUid());
+        }
+
+
+        /**
+         * Any changes from SystemUI or Settings should be fromUser == true. Any changes the
+         * allowlist should be fromUser == false.
+         */
+        @Override
+        @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+        public void setCanBePromoted(String pkg, int uid, boolean promote, boolean fromUser) {
+            checkCallerIsSystemOrSystemUiOrShell();
+            if (!android.app.Flags.apiRichOngoing()) {
+                return;
+            }
+            boolean changed = mPreferencesHelper.setCanBePromoted(pkg, uid, promote, fromUser);
+            if (changed) {
+                // check for pending/posted notifs from this app and update the flag
+                synchronized (mNotificationLock) {
+                    // for enqueued we just need to update the flag
+                    List<NotificationRecord> enqueued = findAppNotificationByListLocked(
+                            mEnqueuedNotifications, pkg, UserHandle.getUserId(uid));
+                    for (NotificationRecord r : enqueued) {
+                        if (promote
+                                && r.getNotification().hasPromotableCharacteristics()
+                                && r.getImportance() > IMPORTANCE_MIN) {
+                            r.getNotification().flags |= FLAG_PROMOTED_ONGOING;
+                        } else if (!promote) {
+                            r.getNotification().flags &= ~FLAG_PROMOTED_ONGOING;
+                        }
+                    }
+                    // if the notification is posted we need to update the flag and tell listeners
+                    List<NotificationRecord> posted = findAppNotificationByListLocked(
+                            mNotificationList, pkg, UserHandle.getUserId(uid));
+                    for (NotificationRecord r : posted) {
+                        if (promote
+                                && !hasFlag(r.getNotification().flags, FLAG_PROMOTED_ONGOING)
+                                && r.getNotification().hasPromotableCharacteristics()
+                                && r.getImportance() > IMPORTANCE_MIN) {
+                            r.getNotification().flags |= FLAG_PROMOTED_ONGOING;
+                            // we could set a wake lock here but this value should only change
+                            // in response to user action, so the device should be awake long enough
+                            // to post
+                            PostNotificationTracker tracker =
+                                    mPostNotificationTrackerFactory.newTracker(null);
+                            // Set false for isAppForeground because that field is only used
+                            // for bubbles and messagingstyle can not be promoted
+                            mHandler.post(new EnqueueNotificationRunnable(
+                                    r.getUser().getIdentifier(),
+                                    r, /* isAppForeground */ false, /* isAppProvided= */ false,
+                                    tracker));
+                        } else if (!promote
+                                && hasFlag(r.getNotification().flags, FLAG_PROMOTED_ONGOING)){
+                            r.getNotification().flags &= ~FLAG_PROMOTED_ONGOING;
+                            PostNotificationTracker tracker =
+                                    mPostNotificationTrackerFactory.newTracker(null);
+                            mHandler.post(new EnqueueNotificationRunnable(
+                                    r.getUser().getIdentifier(),
+                                    r, /* isAppForeground */ false, /* isAppProvided= */ false,
+                                    tracker));
+                        }
+                    }
+                }
+                handleSavePolicyFile();
+            }
+        }
+
+        @Override
         public boolean hasSentValidMsg(String pkg, int uid) {
             checkCallerIsSystem();
             return mPreferencesHelper.hasSentValidMsg(pkg, uid);
@@ -6586,13 +6673,7 @@
             final Uri originalSoundUri =
                     (originalChannel != null) ? originalChannel.getSound() : null;
             if (soundUri != null && !Objects.equals(originalSoundUri, soundUri)) {
-                Binder.withCleanCallingIdentity(() -> {
-                    mUgmInternal.checkGrantUriPermission(sourceUid, null,
-                            ContentProvider.getUriWithoutUserId(soundUri),
-                            Intent.FLAG_GRANT_READ_URI_PERMISSION,
-                            ContentProvider.getUserIdFromUri(soundUri,
-                            UserHandle.getUserId(sourceUid)));
-                });
+                PermissionHelper.grantUriPermission(mUgmInternal, soundUri, sourceUid);
             }
         }
 
@@ -6795,6 +6876,9 @@
             if (notificationForceGrouping()) {
                 if (r.getSbn().isAppGroup()) {
                     mListeners.notifyPostedLocked(r, r);
+
+                    mNotificationRecordLogger.log(
+                        NotificationRecordLogger.NotificationEvent.NOTIFICATION_FORCE_GROUP, r);
                 }
             }
         }
@@ -6975,6 +7059,10 @@
             // Clear summary flag
             StatusBarNotification sbn = r.getSbn();
             sbn.getNotification().flags = (r.mOriginalFlags & ~FLAG_GROUP_SUMMARY);
+
+            EventLogTags.writeNotificationSummaryConverted(key);
+            mNotificationRecordLogger.log(
+                NotificationRecordLogger.NotificationEvent.NOTIFICATION_FORCE_GROUP_SUMMARY, r);
             return true;
         }
         return false;
@@ -7698,6 +7786,16 @@
             return false;
         }
 
+        if (android.app.Flags.apiRichOngoing()) {
+            // This would normally be done in fixNotification(), but we need the channel info so
+            // it's done a little late
+            if (mPreferencesHelper.canBePromoted(pkg, notificationUid)
+                    && notification.hasPromotableCharacteristics()
+                    && channel.getImportance() > IMPORTANCE_MIN) {
+                notification.flags |= FLAG_PROMOTED_ONGOING;
+            }
+        }
+
         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
         r.setIsAppImportanceLocked(mPermissionHelper.isPermissionUserSet(pkg, userId));
         r.setPostSilently(postSilently);
@@ -7938,6 +8036,9 @@
             }
         }
 
+        // Apps cannot set this flag
+         notification.flags &= ~FLAG_PROMOTED_ONGOING;
+
         // Ensure CallStyle has all the correct actions
         if (notification.isStyle(Notification.CallStyle.class)) {
             Notification.Builder builder =
@@ -8061,12 +8162,7 @@
 
     private void checkRemoteViews(String pkg, String tag, int id, Notification notification) {
         if (android.app.Flags.removeRemoteViews()) {
-            if (notification.contentView != null || notification.bigContentView != null
-                    ||  notification.headsUpContentView != null
-                    || (notification.publicVersion != null
-                    && (notification.publicVersion.contentView != null
-                    || notification.publicVersion.bigContentView != null
-                    || notification.publicVersion.headsUpContentView != null))) {
+            if (notification.containsCustomViews()) {
                 Slog.i(TAG, "Removed customViews for " + pkg);
                 mUsageStats.registerImageRemoved(pkg);
             }
@@ -9236,8 +9332,8 @@
             }
         }
 
-        final String oldTitle = String.valueOf(oldN.extras.get(Notification.EXTRA_TITLE));
-        final String newTitle = String.valueOf(newN.extras.get(Notification.EXTRA_TITLE));
+        final String oldTitle = String.valueOf(oldN.extras.get(EXTRA_TITLE));
+        final String newTitle = String.valueOf(newN.extras.get(EXTRA_TITLE));
         if (!Objects.equals(oldTitle, newTitle)) {
             if (DEBUG_INTERRUPTIVENESS) {
                 Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -10654,6 +10750,22 @@
     }
 
     @GuardedBy("mNotificationLock")
+    @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+    private @NonNull List<NotificationRecord> findAppNotificationByListLocked(
+            ArrayList<NotificationRecord> list, String pkg, int userId) {
+        List<NotificationRecord> records = new ArrayList<>();
+        final int len = list.size();
+        for (int i = 0; i < len; i++) {
+            NotificationRecord r = list.get(i);
+            if (notificationMatchesUserId(r, userId, false)
+                    && r.getSbn().getPackageName().equals(pkg)) {
+                records.add(r);
+            }
+        }
+        return records;
+    }
+
+    @GuardedBy("mNotificationLock")
     private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
             ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
         List<NotificationRecord> records = new ArrayList<>();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index b9f0968..3ba9384 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1493,14 +1493,23 @@
 
             final Notification notification = getNotification();
             notification.visitUris((uri) -> {
-                visitGrantableUri(uri, false, false);
+                if (com.android.server.notification.Flags.notificationVerifyChannelSoundUri()) {
+                    visitGrantableUri(uri, false, false);
+                } else {
+                    oldVisitGrantableUri(uri, false, false);
+                }
             });
 
             if (notification.getChannelId() != null) {
                 NotificationChannel channel = getChannel();
                 if (channel != null) {
-                    visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
-                            & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
+                    if (com.android.server.notification.Flags.notificationVerifyChannelSoundUri()) {
+                        visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
+                                & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
+                    } else {
+                        oldVisitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
+                                & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
+                    }
                 }
             }
         } finally {
@@ -1516,7 +1525,7 @@
      * {@link #mGrantableUris}. Otherwise, this will either log or throw
      * {@link SecurityException} depending on target SDK of enqueuing app.
      */
-    private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) {
+    private void oldVisitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) {
         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
 
         if (mGrantableUris != null && mGrantableUris.contains(uri)) {
@@ -1555,6 +1564,45 @@
         }
     }
 
+    /**
+     * Note the presence of a {@link Uri} that should have permission granted to
+     * whoever will be rendering it.
+     * <p>
+     * If the enqueuing app has the ability to grant access, it will be added to
+     * {@link #mGrantableUris}. Otherwise, this will either log or throw
+     * {@link SecurityException} depending on target SDK of enqueuing app.
+     */
+    private void visitGrantableUri(Uri uri, boolean userOverriddenUri,
+            boolean isSound) {
+        if (mGrantableUris != null && mGrantableUris.contains(uri)) {
+            return; // already verified this URI
+        }
+
+        final int sourceUid = getSbn().getUid();
+        try {
+            PermissionHelper.grantUriPermission(mUgmInternal, uri, sourceUid);
+
+            if (mGrantableUris == null) {
+                mGrantableUris = new ArraySet<>();
+            }
+            mGrantableUris.add(uri);
+        } catch (SecurityException e) {
+            if (!userOverriddenUri) {
+                if (isSound) {
+                    mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
+                    Log.w(TAG, "Replacing " + uri + " from " + sourceUid + ": " + e.getMessage());
+                } else {
+                    if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
+                        throw e;
+                    } else {
+                        Log.w(TAG,
+                                "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
     public LogMaker getLogMaker(long now) {
         LogMaker lm = getSbn().getLogMaker()
                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 65ef53f..3943aa5 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -313,7 +313,11 @@
         @UiEvent(doc = "Notification assistant generated notification action at 1 was clicked.")
         NOTIFICATION_ASSIST_ACTION_CLICKED_1(457),
         @UiEvent(doc = "Notification assistant generated notification action at 2 was clicked.")
-        NOTIFICATION_ASSIST_ACTION_CLICKED_2(458);
+        NOTIFICATION_ASSIST_ACTION_CLICKED_2(458),
+        @UiEvent(doc = "Notification was force autogrouped.")
+        NOTIFICATION_FORCE_GROUP(1843),
+        @UiEvent(doc = "Notification summary was force autogrouped.")
+        NOTIFICATION_FORCE_GROUP_SUMMARY(1844);
 
         private final int mId;
         NotificationEvent(int id) {
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index b6f4889..1464d48 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -25,19 +25,25 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.companion.virtual.VirtualDeviceManager;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.permission.IPermissionManager;
 import android.util.ArrayMap;
 import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -58,7 +64,7 @@
     private final IPermissionManager mPermManager;
 
     public PermissionHelper(Context context, IPackageManager packageManager,
-            IPermissionManager permManager) {
+                IPermissionManager permManager) {
         mContext = context;
         mPackageManager = packageManager;
         mPermManager = permManager;
@@ -298,6 +304,19 @@
         return false;
     }
 
+    static void grantUriPermission(final UriGrantsManagerInternal ugmInternal, Uri uri,
+            int sourceUid) {
+        if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
+
+        Binder.withCleanCallingIdentity(() -> {
+            // This will throw a SecurityException if the caller can't grant.
+            ugmInternal.checkGrantUriPermission(sourceUid, null,
+                    ContentProvider.getUriWithoutUserId(uri),
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                    ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
+        });
+    }
+
     public static class PackagePermission {
         public final String packageName;
         public final @UserIdInt int userId;
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index a4fdb75..9e70f81 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -40,7 +40,9 @@
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
+import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_PROMOTABLE;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -92,6 +94,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.notification.PermissionHelper.PackagePermission;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -162,6 +165,7 @@
     private static final String ATT_SENT_VALID_MESSAGE = "sent_valid_msg";
     private static final String ATT_USER_DEMOTED_INVALID_MSG_APP = "user_demote_msg_app";
     private static final String ATT_SENT_VALID_BUBBLE = "sent_valid_bubble";
+    private static final String ATT_PROMOTE_NOTIFS = "promote";
 
     private static final String ATT_CREATION_TIME = "creation_time";
 
@@ -190,10 +194,13 @@
     /**
      * All user-lockable fields for a given application.
      */
-    @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
+    @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE,
+            LockableAppFields.USER_LOCKED_BUBBLE,
+            LockableAppFields.USER_LOCKED_PROMOTABLE})
     public @interface LockableAppFields {
         int USER_LOCKED_IMPORTANCE = 0x00000001;
         int USER_LOCKED_BUBBLE = 0x00000002;
+        int USER_LOCKED_PROMOTABLE = 0x00000004;
     }
 
     private final Object mLock = new Object();
@@ -213,6 +220,7 @@
     private final NotificationChannelLogger mNotificationChannelLogger;
     private final AppOpsManager mAppOps;
     private final ManagedServices.UserProfiles mUserProfiles;
+    private final UriGrantsManagerInternal mUgmInternal;
 
     private SparseBooleanArray mBadgingEnabled;
     private SparseBooleanArray mBubblesEnabled;
@@ -233,6 +241,7 @@
             ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
             NotificationChannelLogger notificationChannelLogger,
             AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles,
+            UriGrantsManagerInternal ugmInternal,
             boolean showReviewPermissionsNotification, Clock clock) {
         mContext = context;
         mZenModeHelper = zenHelper;
@@ -243,6 +252,7 @@
         mNotificationChannelLogger = notificationChannelLogger;
         mAppOps = appOpsManager;
         mUserProfiles = userProfiles;
+        mUgmInternal = ugmInternal;
         mShowReviewPermissionsNotification = showReviewPermissionsNotification;
         mIsMediaNotificationFilteringEnabled = context.getResources()
                 .getBoolean(R.bool.config_quickSettingsShowMediaPlayer);
@@ -351,6 +361,10 @@
             r.userDemotedMsgApp = parser.getAttributeBoolean(
                     null, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
             r.hasSentValidBubble = parser.getAttributeBoolean(null, ATT_SENT_VALID_BUBBLE, false);
+            if (android.app.Flags.uiRichOngoing()) {
+                r.canHavePromotedNotifs =
+                        parser.getAttributeBoolean(null, ATT_PROMOTE_NOTIFS, false);
+            }
 
             final int innerDepth = parser.getDepth();
             int type;
@@ -739,6 +753,11 @@
         out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
                 r.userDemotedMsgApp);
         out.attributeBoolean(null, ATT_SENT_VALID_BUBBLE, r.hasSentValidBubble);
+        if (android.app.Flags.uiRichOngoing()) {
+            if (r.canHavePromotedNotifs) {
+                out.attributeBoolean(null, ATT_PROMOTE_NOTIFS, r.canHavePromotedNotifs);
+            }
+        }
 
         if (Flags.persistIncompleteRestoreData() && r.uid == UNKNOWN_UID) {
             out.attributeLong(null, ATT_CREATION_TIME, r.creationTime);
@@ -839,6 +858,34 @@
         }
     }
 
+    @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public boolean canBePromoted(String packageName, int uid) {
+        synchronized (mLock) {
+            return getOrCreatePackagePreferencesLocked(packageName, uid).canHavePromotedNotifs;
+        }
+    }
+
+    @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public boolean setCanBePromoted(String packageName, int uid, boolean promote,
+            boolean fromUser) {
+        boolean changed = false;
+        synchronized (mLock) {
+            PackagePreferences pkgPrefs = getOrCreatePackagePreferencesLocked(packageName, uid);
+            if (fromUser || ((pkgPrefs.lockedAppFields & USER_LOCKED_PROMOTABLE) == 0)) {
+                if (pkgPrefs.canHavePromotedNotifs != promote) {
+                    pkgPrefs.canHavePromotedNotifs = promote;
+                    if (fromUser) {
+                        pkgPrefs.lockedAppFields |= USER_LOCKED_PROMOTABLE;
+                    }
+                    changed = true;
+                }
+            }
+        }
+        // no need to send a ranking update because we need to update the flag value on all pending
+        // and posted notifs and NMS will take care of that
+        return changed;
+    }
+
     public boolean isInInvalidMsgState(String packageName, int uid) {
         synchronized (mLock) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
@@ -1126,6 +1173,13 @@
                 }
                 clearLockedFieldsLocked(channel);
 
+                // Verify that the app has permission to read the sound Uri
+                // Only check for new channels, as regular apps can only set sound
+                // before creating. See: {@link NotificationChannel#setSound}
+                if (Flags.notificationVerifyChannelSoundUri()) {
+                    PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid);
+                }
+
                 channel.setImportanceLockedByCriticalDeviceFunction(
                         r.defaultAppLockedImportance || r.fixedImportance);
 
@@ -2180,6 +2234,10 @@
                     pw.print(" fixedImportance=");
                     pw.print(r.fixedImportance);
                 }
+                if (android.app.Flags.uiRichOngoing() && r.canHavePromotedNotifs) {
+                    pw.print(" promoted=");
+                    pw.print(r.canHavePromotedNotifs);
+                }
                 pw.println();
                 for (NotificationChannel channel : r.channels.values()) {
                     pw.print(prefix);
@@ -3028,6 +3086,9 @@
         boolean migrateToPm = false;
         long creationTime;
 
+        @FlaggedApi(android.app.Flags.FLAG_API_RICH_ONGOING)
+        boolean canHavePromotedNotifs = false;
+
         @UserIdInt int userId;
 
         Delegate delegate = null;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index e9db1b5..626c3dd 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1358,7 +1358,8 @@
             if (isNew) {
                 // Newly created rule with no provided policy; fill in with the default.
                 zenRule.zenPolicy =
-                        Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy();
+                        (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy())
+                                .copy();
                 return true;
             }
             // Otherwise, a null policy means no policy changes, so we can stop here.
@@ -1721,7 +1722,7 @@
             // booleans to determine whether to reset the rules to the default rules
             boolean allRulesDisabled = true;
             boolean hasDefaultRules = config.automaticRules.containsAll(
-                    ZenModeConfig.DEFAULT_RULE_IDS);
+                    ZenModeConfig.getDefaultRuleIds());
 
             long time = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis();
             if (config.automaticRules != null && config.automaticRules.size() > 0) {
@@ -1773,7 +1774,7 @@
                 // definition cannot have a rule with TYPE_BEDTIME (or any other type).
                 config.automaticRules = new ArrayMap<>();
                 for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
-                    config.automaticRules.put(rule.id, rule);
+                    config.automaticRules.put(rule.id, rule.copy());
                 }
                 reason += ", reset to default rules";
             }
@@ -1798,6 +1799,14 @@
                 config.deletedRules.clear();
             }
 
+            if (Flags.modesUi() && config.automaticRules != null) {
+                ZenRule obsoleteEventsRule = config.automaticRules.get(
+                        ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
+                if (obsoleteEventsRule != null && !obsoleteEventsRule.enabled) {
+                    config.automaticRules.remove(ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
+                }
+            }
+
             if (DEBUG) Log.d(TAG, reason);
             synchronized (mConfigLock) {
                 setConfigLocked(config, null,
@@ -2256,7 +2265,7 @@
     private static void updateRuleStringsForCurrentLocale(Context context,
             ZenModeConfig defaultConfig) {
         for (ZenRule rule : defaultConfig.automaticRules.values()) {
-            if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) {
+            if (ZenModeConfig.EVENTS_OBSOLETE_RULE_ID.equals(rule.id)) {
                 rule.name = context.getResources()
                         .getString(R.string.zen_mode_default_events_name);
             } else if (ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID.equals(rule.id)) {
@@ -2278,7 +2287,7 @@
         }
         ZenPolicy defaultPolicy = defaultConfig.getZenPolicy();
         for (ZenRule rule : defaultConfig.automaticRules.values()) {
-            if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) {
+            if (ZenModeConfig.getDefaultRuleIds().contains(rule.id) && rule.zenPolicy == null) {
                 rule.zenPolicy = defaultPolicy.copy();
             }
         }
@@ -2482,7 +2491,7 @@
             List<StatsEvent> events) {
         // Make the ID safe.
         String id = rule.id == null ? "" : rule.id;
-        if (!ZenModeConfig.DEFAULT_RULE_IDS.contains(id)) {
+        if (!ZenModeConfig.getDefaultRuleIds().contains(id)) {
             id = "";
         }
 
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index be3adc1..0b34177 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -144,6 +144,13 @@
 }
 
 flag {
+  name: "notification_minimalism"
+  namespace: "systemui"
+  description: "Minimize the notifications to show on the lockscreen."
+  bug: "330387368"
+}
+
+flag {
   name: "notification_force_group_singletons"
   namespace: "systemui"
   description: "This flag enables forced auto-grouping singleton groups"
@@ -163,3 +170,13 @@
   description: "This flag enables sound uri with vibration source"
   bug: "358524009"
 }
+
+flag {
+  name: "notification_verify_channel_sound_uri"
+  namespace: "systemui"
+  description: "Verify Uri permission for sound when creating a notification channel"
+  bug: "337775777"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index a41675a..6303ecd 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -298,13 +298,12 @@
 
             restoreSettings();
 
-            if (Build.IS_USER) {
-                // Wipe all shell overlays on boot, to recover from a potentially broken device
-                String shellPkgName = TextUtils.emptyIfNull(
-                        getContext().getString(android.R.string.config_systemShell));
-                mSettings.removeIf(overlayInfo -> overlayInfo.isFabricated
-                        && shellPkgName.equals(overlayInfo.packageName));
-            }
+            // Wipe all shell overlays on boot, to recover from a potentially broken device
+            String shellPkgName = TextUtils.emptyIfNull(
+                    getContext().getString(android.R.string.config_systemShell));
+            mSettings.removeIf(overlayInfo -> overlayInfo.isFabricated
+                    && shellPkgName.equals(overlayInfo.packageName));
+
             initIfNeeded();
             onStartUser(UserHandle.USER_SYSTEM);
 
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index f6d9dc2..03a34f2 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -87,6 +87,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
 import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
 
 import java.io.FileDescriptor;
@@ -194,9 +195,13 @@
 
             mIsServiceEnabled = isServiceEnabled();
         }
+    }
 
-        //connect to remote services(if available) during boot phase.
-        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+    @Override
+    public void onUserUnlocked(@NonNull TargetUser user) {
+        Slog.d(TAG, "onUserUnlocked: " + user.getUserHandle());
+        //connect to remote services(if available) during boot.
+        if(user.getUserHandle().equals(UserHandle.SYSTEM)) {
             try {
                 ensureRemoteInferenceServiceInitialized();
                 ensureRemoteIntelligenceServiceInitialized();
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 8410cff..fe9a859 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -171,7 +171,6 @@
     static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
 
     public static final int DIGEST_SIZE_BYTES = 32;
-    private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
     private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
     private static final String FLASH_LOCK_LOCKED = "1";
     private static final String FLASH_LOCK_UNLOCKED = "0";
@@ -275,7 +274,6 @@
             enforceChecksumValidity();
             if (mFrpEnforced) {
                 automaticallyDeactivateFrpIfPossible();
-                setOemUnlockEnabledProperty(doGetOemUnlockEnabled());
                 setOldSettingForBackworkCompatibility(mFrpActive);
             } else {
                 formatIfOemUnlockEnabled();
@@ -303,10 +301,6 @@
         }
     }
 
-    private void setOemUnlockEnabledProperty(boolean oemUnlockEnabled) {
-        setProperty(OEM_UNLOCK_PROP, oemUnlockEnabled ? "1" : "0");
-    }
-
     @Override
     public void onBootPhase(int phase) {
         // Wait for initialization in onStart to finish
@@ -342,7 +336,6 @@
                 formatPartitionLocked(true);
             }
         }
-        setOemUnlockEnabledProperty(enabled);
     }
 
     private void enforceOemUnlockReadPermission() {
@@ -814,17 +807,9 @@
             channel.force(true);
         } catch (IOException e) {
             Slog.e(TAG, "unable to access persistent partition", e);
-            return;
-        } finally {
-            setOemUnlockEnabledProperty(enabled);
         }
     }
 
-    @VisibleForTesting
-    void setProperty(String name, String value) {
-        SystemProperties.set(name, value);
-    }
-
     private boolean doGetOemUnlockEnabled() {
         DataInputStream inputStream;
         try {
diff --git a/services/core/java/com/android/server/pinner/PinRangeSource.java b/services/core/java/com/android/server/pinner/PinRangeSource.java
new file mode 100644
index 0000000..5f96411
--- /dev/null
+++ b/services/core/java/com/android/server/pinner/PinRangeSource.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pinner;
+
+/* package */ abstract class PinRangeSource {
+    /**
+     * Retrieve a range to pin.
+     *
+     * @param outPinRange Receives the pin region
+     * @return True if we filled in outPinRange or false if we're out of pin entries
+     */
+    abstract boolean read(PinnerService.PinRange outPinRange);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pinner/PinRangeSourceStatic.java b/services/core/java/com/android/server/pinner/PinRangeSourceStatic.java
new file mode 100644
index 0000000..d6fc487
--- /dev/null
+++ b/services/core/java/com/android/server/pinner/PinRangeSourceStatic.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pinner;
+
+/* package */ class PinRangeSourceStatic extends PinRangeSource {
+    private final int mPinStart;
+    private final int mPinLength;
+    private boolean mDone = false;
+
+    PinRangeSourceStatic(int pinStart, int pinLength) {
+        mPinStart = pinStart;
+        mPinLength = pinLength;
+    }
+
+    @Override
+    boolean read(PinnerService.PinRange outPinRange) {
+        outPinRange.start = mPinStart;
+        outPinRange.length = mPinLength;
+        boolean done = mDone;
+        mDone = true;
+        return !done;
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pinner/PinRangeSourceStream.java b/services/core/java/com/android/server/pinner/PinRangeSourceStream.java
new file mode 100644
index 0000000..79900b9
--- /dev/null
+++ b/services/core/java/com/android/server/pinner/PinRangeSourceStream.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pinner;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/* package */ final class PinRangeSourceStream extends PinRangeSource {
+    private final DataInputStream mStream;
+    private boolean mDone = false;
+
+    PinRangeSourceStream(InputStream stream) {
+        mStream = new DataInputStream(stream);
+    }
+
+    @Override
+    boolean read(PinnerService.PinRange outPinRange) {
+        if (!mDone) {
+            try {
+                outPinRange.start = mStream.readInt();
+                outPinRange.length = mStream.readInt();
+            } catch (IOException ex) {
+                mDone = true;
+            }
+        }
+        return !mDone;
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pinner/PinnedFile.java b/services/core/java/com/android/server/pinner/PinnedFile.java
new file mode 100644
index 0000000..a8de344
--- /dev/null
+++ b/services/core/java/com/android/server/pinner/PinnedFile.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pinner;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+
+@VisibleForTesting
+public final class PinnedFile implements AutoCloseable {
+    private long mAddress;
+    final long mapSize;
+    final String fileName;
+    public final long bytesPinned;
+
+    // Whether this file was pinned using a pinlist
+    boolean used_pinlist;
+
+    // User defined group name for pinner accounting
+    String groupName = "";
+    ArrayList<PinnedFile> pinnedDeps = new ArrayList<>();
+
+    public PinnedFile(long address, long mapSize, String fileName, long bytesPinned) {
+        mAddress = address;
+        this.mapSize = mapSize;
+        this.fileName = fileName;
+        this.bytesPinned = bytesPinned;
+    }
+
+    @Override
+    public void close() {
+        if (mAddress >= 0) {
+            PinnerUtils.safeMunmap(mAddress, mapSize);
+            mAddress = -1;
+        }
+        for (PinnedFile dep : pinnedDeps) {
+            if (dep != null) {
+                dep.close();
+            }
+        }
+    }
+
+    @Override
+    public void finalize() {
+        close();
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java
similarity index 76%
rename from services/core/java/com/android/server/PinnerService.java
rename to services/core/java/com/android/server/pinner/PinnerService.java
index ef03888..d7ac520 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/pinner/PinnerService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.pinner;
 
 import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
 import static android.app.ActivityManager.UID_OBSERVER_GONE;
 import static android.os.Process.SYSTEM_UID;
 
+import static com.android.server.flags.Flags.pinGlobalQuota;
 import static com.android.server.flags.Flags.pinWebview;
-import static com.android.server.flags.Flags.skipHomeArtPins;
 
 import android.annotation.EnforcePermission;
 import android.annotation.IntDef;
@@ -49,6 +49,7 @@
 import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
@@ -72,13 +73,13 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
 
-import java.io.Closeable;
-import java.io.DataInputStream;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -110,8 +111,7 @@
     private static final String PIN_META_FILENAME = "pinlist.meta";
     private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
     private static final int MATCH_FLAGS = PackageManager.MATCH_DEFAULT_ONLY
-            | PackageManager.MATCH_DIRECT_BOOT_AWARE
-            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+            | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
     private static final int KEY_CAMERA = 0;
     private static final int KEY_HOME = 1;
@@ -126,6 +126,8 @@
 
     public static final String ANON_REGION_STAT_NAME = "[anon]";
 
+    private static final String SYSTEM_GROUP_NAME = "system";
+
     @IntDef({KEY_CAMERA, KEY_HOME, KEY_ASSISTANT})
     @Retention(RetentionPolicy.SOURCE)
     public @interface AppKey {}
@@ -139,7 +141,8 @@
     private final UserManager mUserManager;
 
     /** The list of the statically pinned files. */
-    @GuardedBy("this") private final ArrayMap<String, PinnedFile> mPinnedFiles = new ArrayMap<>();
+    @GuardedBy("this")
+    private final ArrayMap<String, PinnedFile> mPinnedFiles = new ArrayMap<>();
 
     /** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
     @GuardedBy("this")
@@ -159,8 +162,8 @@
     /**
      * A set of {@link AppKey} that are configured to be pinned.
      */
-    @GuardedBy("this")
-    private ArraySet<Integer> mPinKeys;
+    @GuardedBy("this") private
+    ArraySet<Integer> mPinKeys;
 
     // Note that we don't use the `_BOOT` namespace for anonymous pinnings, as we want
     // them to be responsive to dynamic flag changes for experimentation.
@@ -180,14 +183,23 @@
     private final boolean mConfiguredToPinAssistant;
     private final int mConfiguredWebviewPinBytes;
 
+    // This is the percentage of total device memory that will be used to set the global quota.
+    private final int mConfiguredMaxPinnedMemoryPercentage;
+
+    // This is the global pinner quota that can be pinned.
+    private long mConfiguredMaxPinnedMemory;
+
+    // This is the currently pinned memory.
+    private long mCurrentPinnedMemory = 0;
+
     private BinderService mBinderService;
     private PinnerHandler mPinnerHandler = null;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-          // If an app has updated, update pinned files accordingly.
-          if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
+            // If an app has updated, update pinned files accordingly.
+            if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
                 Uri packageUri = intent.getData();
                 String packageName = packageUri.getSchemeSpecificPart();
                 ArraySet<String> updatedPackages = new ArraySet<>();
@@ -210,7 +222,7 @@
 
     /** Utility class for testing. */
     @VisibleForTesting
-    static class Injector {
+    public static class Injector {
         protected DeviceConfigInterface getDeviceConfigInterface() {
             return DeviceConfigInterface.REAL;
         }
@@ -219,9 +231,9 @@
             service.publishBinderService("pinner", binderService);
         }
 
-        protected PinnedFile pinFileInternal(String fileToPin,
-                int maxBytesToPin, boolean attemptPinIntrospection) {
-            return PinnerService.pinFileInternal(fileToPin, maxBytesToPin, attemptPinIntrospection);
+        protected PinnedFile pinFileInternal(PinnerService service, String fileToPin,
+                long maxBytesToPin, boolean attemptPinIntrospection) {
+            return service.pinFileInternal(fileToPin, maxBytesToPin, attemptPinIntrospection);
         }
     }
 
@@ -230,7 +242,7 @@
     }
 
     @VisibleForTesting
-    PinnerService(Context context, Injector injector) {
+    public PinnerService(Context context, Injector injector) {
         super(context);
 
         mContext = context;
@@ -244,6 +256,9 @@
                 com.android.internal.R.bool.config_pinnerAssistantApp);
         mConfiguredWebviewPinBytes = context.getResources().getInteger(
                 com.android.internal.R.integer.config_pinnerWebviewPinBytes);
+        mConfiguredMaxPinnedMemoryPercentage = context.getResources().getInteger(
+                com.android.internal.R.integer.config_pinnerMaxPinnedMemoryPercentage);
+
         mPinKeys = createPinKeys();
         mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
 
@@ -261,10 +276,8 @@
         registerUidListener();
         registerUserSetupCompleteListener();
 
-        mDeviceConfigInterface.addOnPropertiesChangedListener(
-                DEVICE_CONFIG_NAMESPACE_ANON_SIZE,
-                new HandlerExecutor(mPinnerHandler),
-                mDeviceConfigAnonSizeListener);
+        mDeviceConfigInterface.addOnPropertiesChangedListener(DEVICE_CONFIG_NAMESPACE_ANON_SIZE,
+                new HandlerExecutor(mPinnerHandler), mDeviceConfigAnonSizeListener);
     }
 
     @Override
@@ -272,6 +285,10 @@
         if (DEBUG) {
             Slog.i(TAG, "Starting PinnerService");
         }
+        mConfiguredMaxPinnedMemory =
+                (Process.getTotalMemory()
+                        * Math.clamp(mConfiguredMaxPinnedMemoryPercentage, 0, 100))
+                / 100;
         mBinderService = new BinderService();
         mInjector.publishBinderService(this, mBinderService);
         publishLocalService(PinnerService.class, this);
@@ -348,7 +365,7 @@
         protected PinnedFileStats(int uid, PinnedFile file) {
             this.uid = uid;
             this.filename = file.fileName.substring(file.fileName.lastIndexOf('/') + 1);
-            this.sizeKb = file.bytesPinned / 1024;
+            this.sizeKb = (int) file.bytesPinned / 1024;
         }
     }
 
@@ -358,20 +375,11 @@
     private void handlePinOnStart() {
         // Files to pin come from the overlay and can be specified per-device config
         String[] filesToPin = mContext.getResources().getStringArray(
-            com.android.internal.R.array.config_defaultPinnerServiceFiles);
+                com.android.internal.R.array.config_defaultPinnerServiceFiles);
         // Continue trying to pin each file even if we fail to pin some of them
         for (String fileToPin : filesToPin) {
-            PinnedFile pf = mInjector.pinFileInternal(fileToPin, Integer.MAX_VALUE,
-                    /*attemptPinIntrospection=*/false);
-            if (pf == null) {
-                Slog.e(TAG, "Failed to pin file = " + fileToPin);
-                continue;
-            }
-            synchronized (this) {
-                mPinnedFiles.put(pf.fileName, pf);
-            }
-            pf.groupName = "system";
-            pinOptimizedDexDependencies(pf, Integer.MAX_VALUE, null);
+            pinFile(fileToPin, Integer.MAX_VALUE, /*appInfo=*/null, /*groupName=*/SYSTEM_GROUP_NAME,
+                    true);
         }
 
         refreshPinAnonConfig();
@@ -383,10 +391,9 @@
      * regular home app.
      */
     private void registerUserSetupCompleteListener() {
-        Uri userSetupCompleteUri = Settings.Secure.getUriFor(
-                Settings.Secure.USER_SETUP_COMPLETE);
-        mContext.getContentResolver().registerContentObserver(userSetupCompleteUri,
-                false, new ContentObserver(null) {
+        Uri userSetupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE);
+        mContext.getContentResolver().registerContentObserver(
+                userSetupCompleteUri, false, new ContentObserver(null) {
                     @Override
                     public void onChange(boolean selfChange, Uri uri) {
                         if (userSetupCompleteUri.equals(uri)) {
@@ -409,7 +416,7 @@
                 }
 
                 @Override
-                public void onUidActive(int uid)  {
+                public void onUidActive(int uid) {
                     mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
                             PinnerService::handleUidActive, PinnerService.this, uid));
                 }
@@ -423,7 +430,6 @@
         updateActiveState(uid, false /* active */);
         int key;
         synchronized (this) {
-
             // In case we have a pending repin, repin now. See mPendingRepin for more information.
             key = mPendingRepin.getOrDefault(uid, -1);
             if (key == -1) {
@@ -491,8 +497,8 @@
 
     private ApplicationInfo getCameraInfo(int userHandle) {
         Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
-        ApplicationInfo info = getApplicationInfoForIntent(cameraIntent, userHandle,
-            false /* defaultToSystemApp */);
+        ApplicationInfo info = getApplicationInfoForIntent(
+                cameraIntent, userHandle, false /* defaultToSystemApp */);
 
         // If the STILL_IMAGE_CAMERA intent doesn't resolve, try the _SECURE intent.
         // We don't use _SECURE first because it will never get set on a device
@@ -501,16 +507,16 @@
         // preference using this intent.
         if (info == null) {
             cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
-            info = getApplicationInfoForIntent(cameraIntent, userHandle,
-                false /* defaultToSystemApp */);
+            info = getApplicationInfoForIntent(
+                    cameraIntent, userHandle, false /* defaultToSystemApp */);
         }
 
         // If the _SECURE intent doesn't resolve, try the original intent but request
         // the system app for camera if there was more than one result.
         if (info == null) {
             cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
-            info = getApplicationInfoForIntent(cameraIntent, userHandle,
-                true /* defaultToSystemApp */);
+            info = getApplicationInfoForIntent(
+                    cameraIntent, userHandle, true /* defaultToSystemApp */);
         }
         return info;
     }
@@ -525,14 +531,14 @@
         return getApplicationInfoForIntent(intent, userHandle, true);
     }
 
-    private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle,
-            boolean defaultToSystemApp) {
+    private ApplicationInfo getApplicationInfoForIntent(
+            Intent intent, int userHandle, boolean defaultToSystemApp) {
         if (intent == null) {
             return null;
         }
 
-        ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(intent,
-                MATCH_FLAGS, userHandle);
+        ResolveInfo resolveInfo =
+                mContext.getPackageManager().resolveActivityAsUser(intent, MATCH_FLAGS, userHandle);
 
         // If this intent can resolve to only one app, choose that one.
         // Otherwise, if we've requested to default to the system app, return it;
@@ -547,12 +553,11 @@
         }
 
         if (defaultToSystemApp) {
-            List<ResolveInfo> infoList = mContext.getPackageManager()
-                .queryIntentActivitiesAsUser(intent, MATCH_FLAGS, userHandle);
+            List<ResolveInfo> infoList = mContext.getPackageManager().queryIntentActivitiesAsUser(
+                    intent, MATCH_FLAGS, userHandle);
             ApplicationInfo systemAppInfo = null;
             for (ResolveInfo info : infoList) {
-                if ((info.activityInfo.applicationInfo.flags
-                      & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                if ((info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                     if (systemAppInfo == null) {
                         systemAppInfo = info.activityInfo.applicationInfo;
                     } else {
@@ -568,13 +573,13 @@
     }
 
     private void sendPinAppsMessage(int userHandle) {
-        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
-                userHandle));
+        mPinnerHandler.sendMessage(
+                PooledLambda.obtainMessage(PinnerService::pinApps, this, userHandle));
     }
 
     private void sendPinAppsWithUpdatedKeysMessage(int userHandle) {
-        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinAppsWithUpdatedKeys,
-                this, userHandle));
+        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
+                PinnerService::pinAppsWithUpdatedKeys, this, userHandle));
     }
     private void sendUnpinAppsMessage() {
         mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::unpinApps, this));
@@ -586,8 +591,7 @@
         // phenotype property is not set.
         boolean shouldPinCamera = mConfiguredToPinCamera
                 && mDeviceConfigInterface.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
-                        "pin_camera",
-                        SystemProperties.getBoolean("pinner.pin_camera", true));
+                        "pin_camera", SystemProperties.getBoolean("pinner.pin_camera", true));
         if (shouldPinCamera) {
             pinKeys.add(KEY_CAMERA);
         } else if (DEBUG) {
@@ -626,8 +630,9 @@
             synchronized (this) {
                 // This code path demands preceding unpinApps() call.
                 if (!mPinnedApps.isEmpty()) {
-                    Slog.e(TAG, "Attempted to update a list of apps, "
-                            + "but apps were already pinned. Skipping.");
+                    Slog.e(TAG,
+                            "Attempted to update a list of apps, "
+                                    + "but apps were already pinned. Skipping.");
                     return;
                 }
 
@@ -646,8 +651,8 @@
      * @see #pinApp(int, int, boolean)
      */
     private void sendPinAppMessage(int key, int userHandle, boolean force) {
-        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
-                key, userHandle, force));
+        mPinnerHandler.sendMessage(
+                PooledLambda.obtainMessage(PinnerService::pinApp, this, key, userHandle, force));
     }
 
     /**
@@ -667,10 +672,10 @@
             }
             return;
         }
-        unpinApp(key);
         ApplicationInfo info = getInfoForKey(key, userHandle);
+        unpinApp(key);
         if (info != null) {
-            pinApp(key, info);
+            pinAppInternal(key, info);
         }
     }
 
@@ -682,9 +687,7 @@
     private int getUidForKey(@AppKey int key) {
         synchronized (this) {
             PinnedApp existing = mPinnedApps.get(key);
-            return existing != null && existing.active
-                    ? existing.uid
-                    : -1;
+            return existing != null && existing.active ? existing.uid : -1;
         }
     }
 
@@ -727,11 +730,8 @@
      * Handle any changes in the anon region pinner config.
      */
     private void refreshPinAnonConfig() {
-        long newPinAnonSize =
-                mDeviceConfigInterface.getLong(
-                        DEVICE_CONFIG_NAMESPACE_ANON_SIZE,
-                        DEVICE_CONFIG_KEY_ANON_SIZE,
-                        DEFAULT_ANON_SIZE);
+        long newPinAnonSize = mDeviceConfigInterface.getLong(
+                DEVICE_CONFIG_NAMESPACE_ANON_SIZE, DEVICE_CONFIG_KEY_ANON_SIZE, DEFAULT_ANON_SIZE);
         newPinAnonSize = Math.max(0, Math.min(newPinAnonSize, MAX_ANON_SIZE));
         if (newPinAnonSize != mPinAnonSize) {
             mPinAnonSize = newPinAnonSize;
@@ -765,10 +765,9 @@
         try {
             // Map as SHARED to avoid changing rss.anon for system_server (per /proc/*/status).
             // The mapping is visible in other rss metrics, and as private dirty in smaps/meminfo.
-            address = Os.mmap(0, alignedPinSize,
-                    OsConstants.PROT_READ | OsConstants.PROT_WRITE,
-                    OsConstants.MAP_SHARED | OsConstants.MAP_ANONYMOUS,
-                    new FileDescriptor(), /*offset=*/0);
+            address = Os.mmap(0, alignedPinSize, OsConstants.PROT_READ | OsConstants.PROT_WRITE,
+                    OsConstants.MAP_SHARED | OsConstants.MAP_ANONYMOUS, new FileDescriptor(),
+                    /*offset=*/0);
 
             Unsafe tempUnsafe = null;
             Class<sun.misc.Unsafe> clazz = sun.misc.Unsafe.class;
@@ -794,14 +793,14 @@
             return;
         } finally {
             if (address >= 0) {
-                safeMunmap(address, alignedPinSize);
+                PinnerUtils.safeMunmap(address, alignedPinSize);
             }
         }
     }
 
     private void unpinAnonRegion() {
         if (mPinAnonAddress != 0) {
-            safeMunmap(mPinAnonAddress, mCurrentlyPinnedAnonSize);
+            PinnerUtils.safeMunmap(mPinAnonAddress, mCurrentlyPinnedAnonSize);
         }
         mPinAnonAddress = 0;
         mCurrentlyPinnedAnonSize = 0;
@@ -824,12 +823,20 @@
     }
 
     /**
+     * Retrieves remaining quota for pinner service, once it reaches 0 it will no longer
+     * pin any file.
+     */
+    private long getAvailableGlobalQuota() {
+        return mConfiguredMaxPinnedMemory - mCurrentPinnedMemory;
+    }
+
+    /**
      * Pins an application.
      *
      * @param key The key of the app to pin.
      * @param appInfo The corresponding app info.
      */
-    private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
+    private void pinAppInternal(@AppKey int key, @Nullable ApplicationInfo appInfo) {
         if (appInfo == null) {
             return;
         }
@@ -839,7 +846,6 @@
             mPinnedApps.put(key, pinnedApp);
         }
 
-
         // pin APK
         final int pinSizeLimit = getSizeLimitForKey(key);
         List<String> apks = new ArrayList<>();
@@ -851,36 +857,31 @@
             }
         }
 
-        int apkPinSizeLimit = pinSizeLimit;
+        long apkPinSizeLimit = pinSizeLimit;
 
-        boolean shouldSkipArtPins = key == KEY_HOME && skipHomeArtPins();
-
-        for (String apk: apks) {
+        for (String apk : apks) {
             if (apkPinSizeLimit <= 0) {
                 Slog.w(TAG, "Reached to the pin size limit. Skipping: " + apk);
                 // Continue instead of break to print all skipped APK names.
                 continue;
             }
 
-            PinnedFile pf = mInjector.pinFileInternal(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
+            String pinGroup = getNameForKey(key);
+            boolean shouldPinDeps = apk.equals(appInfo.sourceDir);
+            PinnedFile pf = pinFile(apk, apkPinSizeLimit, appInfo, pinGroup, shouldPinDeps);
             if (pf == null) {
                 Slog.e(TAG, "Failed to pin " + apk);
                 continue;
             }
-            pf.groupName = getNameForKey(key);
 
             if (DEBUG) {
                 Slog.i(TAG, "Pinned " + pf.fileName);
             }
             synchronized (this) {
                 pinnedApp.mFiles.add(pf);
-                mPinnedFiles.put(pf.fileName, pf);
             }
 
             apkPinSizeLimit -= pf.bytesPinned;
-            if (apk.equals(appInfo.sourceDir) && !shouldSkipArtPins) {
-                pinOptimizedDexDependencies(pf, Integer.MAX_VALUE, appInfo);
-            }
         }
     }
 
@@ -892,19 +893,23 @@
      * that related to the file but not within itself.
      *
      * @param fileToPin File to pin
-     * @param maxBytesToPin maximum quota allowed for pinning
-     * @return total bytes that were pinned.
+     * @param bytesRequestedToPin maximum bytes requested to pin for {@code fileToPin}.
+     * @param pinOptimizedDeps whether optimized dependencies such as odex,vdex, etc be pinned.
+     *                         Note: {@code bytesRequestedToPin} limit will not apply to optimized
+     *                         dependencies pinned, only global quotas will apply instead.
+     * @return pinned file
      */
-    public int pinFile(String fileToPin, int maxBytesToPin, @Nullable ApplicationInfo appInfo,
-            @Nullable String groupName) {
+    public PinnedFile pinFile(String fileToPin, long bytesRequestedToPin,
+            @Nullable ApplicationInfo appInfo, @Nullable String groupName,
+            boolean pinOptimizedDeps) {
         PinnedFile existingPin;
-        synchronized(this) {
+        synchronized (this) {
             existingPin = mPinnedFiles.get(fileToPin);
         }
         if (existingPin != null) {
-            if (existingPin.bytesPinned == maxBytesToPin) {
+            if (existingPin.bytesPinned == bytesRequestedToPin) {
                 // Duplicate pin requesting same amount of bytes, lets just bail out.
-                return 0;
+                return null;
             } else {
                 // User decided to pin a different amount of bytes than currently pinned
                 // so this is a valid pin request. Unpin the previous version before repining.
@@ -915,26 +920,38 @@
             }
         }
 
+        long remainingQuota = getAvailableGlobalQuota();
+
+        if (pinGlobalQuota()) {
+            if (remainingQuota <= 0) {
+                Slog.w(TAG, "Reached pin quota, skipping file: " + fileToPin);
+                return null;
+            }
+            bytesRequestedToPin = Math.min(bytesRequestedToPin, remainingQuota);
+        }
+
         boolean isApk = fileToPin.endsWith(".apk");
-        int bytesPinned = 0;
-        PinnedFile pf = mInjector.pinFileInternal(fileToPin, maxBytesToPin,
+
+        PinnedFile pf = mInjector.pinFileInternal(this, fileToPin, bytesRequestedToPin,
                 /*attemptPinIntrospection=*/isApk);
         if (pf == null) {
             Slog.e(TAG, "Failed to pin file = " + fileToPin);
-            return 0;
+            return null;
         }
         pf.groupName = groupName != null ? groupName : "";
 
-        bytesPinned += pf.bytesPinned;
-        maxBytesToPin -= bytesPinned;
+        mCurrentPinnedMemory += pf.bytesPinned;
 
         synchronized (this) {
             mPinnedFiles.put(pf.fileName, pf);
         }
-        if (maxBytesToPin > 0) {
-            pinOptimizedDexDependencies(pf, maxBytesToPin, appInfo);
+
+        if (pinOptimizedDeps) {
+            mCurrentPinnedMemory +=
+                    pinOptimizedDexDependencies(pf, getAvailableGlobalQuota(), appInfo);
         }
-        return bytesPinned;
+
+        return pf;
     }
 
     /**
@@ -945,13 +962,13 @@
      *                to null it will use the default supported ABI by the device.
      * @return total bytes pinned.
      */
-    private int pinOptimizedDexDependencies(
-            PinnedFile pinnedFile, int maxBytesToPin, @Nullable ApplicationInfo appInfo) {
+    private long pinOptimizedDexDependencies(
+            PinnedFile pinnedFile, long maxBytesToPin, @Nullable ApplicationInfo appInfo) {
         if (pinnedFile == null) {
             return 0;
         }
 
-        int bytesPinned = 0;
+        long bytesPinned = 0;
         if (pinnedFile.fileName.endsWith(".jar") | pinnedFile.fileName.endsWith(".apk")) {
             String abi = null;
             if (appInfo != null) {
@@ -974,7 +991,7 @@
                 // Unpin if it was already pinned prior to re-pinning.
                 unpinFile(file);
 
-                PinnedFile df = mInjector.pinFileInternal(file, maxBytesToPin,
+                PinnedFile df = mInjector.pinFileInternal(this, file, maxBytesToPin,
                         /*attemptPinIntrospection=*/false);
                 if (df == null) {
                     Slog.i(TAG, "Failed to pin ART file = " + file);
@@ -992,7 +1009,8 @@
         return bytesPinned;
     }
 
-    /** mlock length bytes of fileToPin in memory
+    /**
+     * mlock length bytes of fileToPin in memory
      *
      * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
      * look for a "pinlist.meta" file in the archive root directory. The structure of this
@@ -1029,8 +1047,8 @@
      *   zip in order to extract the
      * @return Pinned memory resource owner thing or null on error
      */
-    private static PinnedFile pinFileInternal(
-            String fileToPin, int maxBytesToPin, boolean attemptPinIntrospection) {
+    private PinnedFile pinFileInternal(
+            String fileToPin, long maxBytesToPin, boolean attemptPinIntrospection) {
         if (DEBUG) {
             Slog.d(TAG, "pin file: " + fileToPin + " use-pinlist: " + attemptPinIntrospection);
         }
@@ -1054,8 +1072,8 @@
             }
             return pinnedFile;
         } finally {
-            safeClose(pinRangeStream);
-            safeClose(fileAsZip);  // Also closes any streams we've opened
+            PinnerUtils.safeClose(pinRangeStream);
+            PinnerUtils.safeClose(fileAsZip); // Also closes any streams we've opened
         }
     }
 
@@ -1068,11 +1086,8 @@
         try {
             zip = new ZipFile(fileName);
         } catch (IOException ex) {
-            Slog.w(TAG,
-                   String.format(
-                       "could not open \"%s\" as zip: pinning as blob",
-                                 fileName),
-                   ex);
+            Slog.w(TAG, String.format("could not open \"%s\" as zip: pinning as blob", fileName),
+                    ex);
         }
         return zip;
     }
@@ -1112,9 +1127,9 @@
                 pinMetaStream = zipFile.getInputStream(pinMetaEntry);
             } catch (IOException ex) {
                 Slog.w(TAG,
-                       String.format("error reading pin metadata \"%s\": pinning as blob",
-                                     fileName),
-                       ex);
+                        String.format(
+                                "error reading pin metadata \"%s\": pinning as blob", fileName),
+                        ex);
             }
         } else {
             Slog.w(TAG,
@@ -1124,57 +1139,6 @@
         return pinMetaStream;
     }
 
-    private static abstract class PinRangeSource {
-        /** Retrive a range to pin.
-         *
-         * @param outPinRange Receives the pin region
-         * @return True if we filled in outPinRange or false if we're out of pin entries
-         */
-        abstract boolean read(PinRange outPinRange);
-    }
-
-    private static final class PinRangeSourceStatic extends PinRangeSource {
-        private final int mPinStart;
-        private final int mPinLength;
-        private boolean mDone = false;
-
-        PinRangeSourceStatic(int pinStart, int pinLength) {
-            mPinStart = pinStart;
-            mPinLength = pinLength;
-        }
-
-        @Override
-        boolean read(PinRange outPinRange) {
-            outPinRange.start = mPinStart;
-            outPinRange.length = mPinLength;
-            boolean done = mDone;
-            mDone = true;
-            return !done;
-        }
-    }
-
-    private static final class PinRangeSourceStream extends PinRangeSource {
-        private final DataInputStream mStream;
-        private boolean mDone = false;
-
-        PinRangeSourceStream(InputStream stream) {
-            mStream = new DataInputStream(stream);
-        }
-
-        @Override
-        boolean read(PinRange outPinRange) {
-            if (!mDone) {
-                try {
-                    outPinRange.start = mStream.readInt();
-                    outPinRange.length = mStream.readInt();
-                } catch (IOException ex) {
-                    mDone = true;
-                }
-            }
-            return !mDone;
-        }
-    }
-
     /**
      * Helper for pinFile.
      *
@@ -1185,25 +1149,20 @@
      * @return PinnedFile or null on error
      */
     private static PinnedFile pinFileRanges(
-        String fileToPin,
-        int maxBytesToPin,
-        PinRangeSource pinRangeSource)
-    {
+            String fileToPin, long maxBytesToPin, PinRangeSource pinRangeSource) {
         FileDescriptor fd = new FileDescriptor();
         long address = -1;
-        int mapSize = 0;
+        long mapSize = 0;
 
         try {
             int openFlags = (OsConstants.O_RDONLY | OsConstants.O_CLOEXEC);
             fd = Os.open(fileToPin, openFlags, 0);
             mapSize = (int) Math.min(Os.fstat(fd).st_size, Integer.MAX_VALUE);
-            address = Os.mmap(0, mapSize,
-                              OsConstants.PROT_READ,
-                              OsConstants.MAP_SHARED,
-                              fd, /*offset=*/0);
+            address = Os.mmap(
+                    0, mapSize, OsConstants.PROT_READ, OsConstants.MAP_SHARED, fd, /*offset=*/0);
 
             PinRange pinRange = new PinRange();
-            int bytesPinned = 0;
+            long bytesPinned = 0;
 
             // We pin at page granularity, so make sure the limit is page-aligned
             if (maxBytesToPin % PAGE_SIZE != 0) {
@@ -1211,10 +1170,10 @@
             }
 
             while (bytesPinned < maxBytesToPin && pinRangeSource.read(pinRange)) {
-                int pinStart = pinRange.start;
-                int pinLength = pinRange.length;
-                pinStart = clamp(0, pinStart, mapSize);
-                pinLength = clamp(0, pinLength, mapSize - pinStart);
+                long pinStart = pinRange.start;
+                long pinLength = pinRange.length;
+                pinStart = PinnerUtils.clamp(0, pinStart, mapSize);
+                pinLength = PinnerUtils.clamp(0, pinLength, mapSize - pinStart);
                 pinLength = Math.min(maxBytesToPin - bytesPinned, pinLength);
 
                 // mlock doesn't require the region to be page-aligned, but we snap the
@@ -1229,14 +1188,13 @@
                 if (pinLength % PAGE_SIZE != 0) {
                     pinLength += PAGE_SIZE - pinLength % PAGE_SIZE;
                 }
-                pinLength = clamp(0, pinLength, maxBytesToPin - bytesPinned);
+                pinLength = PinnerUtils.clamp(0, pinLength, maxBytesToPin - bytesPinned);
 
                 if (pinLength > 0) {
                     if (DEBUG) {
                         Slog.d(TAG,
-                               String.format(
-                                   "pinning at %s %s bytes of %s",
-                                   pinStart, pinLength, fileToPin));
+                                String.format("pinning at %s %s bytes of %s", pinStart, pinLength,
+                                        fileToPin));
                     }
                     Os.mlock(address + pinStart, pinLength);
                 }
@@ -1244,15 +1202,15 @@
             }
 
             PinnedFile pinnedFile = new PinnedFile(address, mapSize, fileToPin, bytesPinned);
-            address = -1;  // Ownership transferred
+            address = -1; // Ownership transferred
             return pinnedFile;
         } catch (ErrnoException ex) {
             Slog.e(TAG, "Could not pin file " + fileToPin, ex);
             return null;
         } finally {
-            safeClose(fd);
+            PinnerUtils.safeClose(fd);
             if (address >= 0) {
-                safeMunmap(address, mapSize);
+                PinnerUtils.safeMunmap(address, mapSize);
             }
         }
     }
@@ -1273,81 +1231,50 @@
         }
     }
 
-    public void unpinFile(String filename) {
+    /**
+     * Unpin a file and its optimized dependencies.
+     *
+     * @param filename file to unpin.
+     * @return number of bytes unpinned, 0 in case of failure or nothing to unpin.
+     */
+    public long unpinFile(String filename) {
         PinnedFile pinnedFile;
         synchronized (this) {
             pinnedFile = mPinnedFiles.get(filename);
         }
         if (pinnedFile == null) {
             // File not pinned, nothing to do.
-            return;
+            return 0;
         }
+        long unpinnedBytes = pinnedFile.bytesPinned;
         pinnedFile.close();
         synchronized (this) {
             if (DEBUG) {
                 Slog.d(TAG, "Unpinned file: " + filename);
             }
+            mCurrentPinnedMemory -= pinnedFile.bytesPinned;
+
             mPinnedFiles.remove(pinnedFile.fileName);
             for (PinnedFile dep : pinnedFile.pinnedDeps) {
                 if (dep == null) {
                     continue;
                 }
+                unpinnedBytes -= dep.bytesPinned;
+                mCurrentPinnedMemory -= dep.bytesPinned;
                 mPinnedFiles.remove(dep.fileName);
                 if (DEBUG) {
                     Slog.d(TAG, "Unpinned dependency: " + dep.fileName);
                 }
             }
         }
-    }
 
-    private static int clamp(int min, int value, int max) {
-        return Math.max(min, Math.min(value, max));
-    }
-
-    private static void safeMunmap(long address, long mapSize) {
-        try {
-            Os.munmap(address, mapSize);
-        } catch (ErrnoException ex) {
-            Slog.w(TAG, "ignoring error in unmap", ex);
-        }
-    }
-
-    /**
-     * Close FD, swallowing irrelevant errors.
-     */
-    private static void safeClose(@Nullable FileDescriptor fd) {
-        if (fd != null && fd.valid()) {
-            try {
-                Os.close(fd);
-            } catch (ErrnoException ex) {
-                // Swallow the exception: non-EBADF errors in close(2)
-                // indicate deferred paging write errors, which we
-                // don't care about here. The underlying file
-                // descriptor is always closed.
-                if (ex.errno == OsConstants.EBADF) {
-                    throw new AssertionError(ex);
-                }
-            }
-        }
-    }
-
-    /**
-     * Close closeable thing, swallowing errors.
-     */
-    private static void safeClose(@Nullable Closeable thing) {
-        if (thing != null) {
-            try {
-                thing.close();
-            } catch (IOException ex) {
-                Slog.w(TAG, "ignoring error closing resource: " + thing, ex);
-            }
-        }
+        return unpinnedBytes;
     }
 
     public List<PinnedFileStat> getPinnerStats() {
         ArrayList<PinnedFileStat> stats = new ArrayList<>();
         Collection<PinnedFile> pinnedFiles;
-        synchronized(this) {
+        synchronized (this) {
             pinnedFiles = mPinnedFiles.values();
         }
         for (PinnedFile pf : pinnedFiles) {
@@ -1355,8 +1282,8 @@
             stats.add(stat);
         }
         if (mCurrentlyPinnedAnonSize > 0) {
-            stats.add(new PinnedFileStat(ANON_REGION_STAT_NAME,
-                        mCurrentlyPinnedAnonSize, ANON_REGION_STAT_NAME));
+            stats.add(new PinnedFileStat(
+                    ANON_REGION_STAT_NAME, mCurrentlyPinnedAnonSize, ANON_REGION_STAT_NAME));
         }
         return stats;
     }
@@ -1364,71 +1291,124 @@
     public final class BinderService extends IPinnerService.Stub {
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw))
+                return;
             HashSet<PinnedFile> shownPins = new HashSet<>();
-            HashSet<String> groups = new HashSet<>();
-            final int bytesPerMB = 1024 * 1024;
+            HashSet<String> shownGroups = new HashSet<>();
+            HashSet<String> groupsToPrint = new HashSet<>();
+            final double bytesPerMB = 1024 * 1024;
+            pw.format("Pinner Configs:\n");
+            pw.format("   Total Pinner quota: %d%% of total device memory\n",
+                    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("\nPinned Files:\n");
             synchronized (PinnerService.this) {
                 long totalSize = 0;
+
+                // We print apps separately from regular pins as they contain extra information that
+                // other pins do not.
                 for (int key : mPinnedApps.keySet()) {
                     PinnedApp app = mPinnedApps.get(key);
                     pw.print(getNameForKey(key));
-                    pw.print(" uid="); pw.print(app.uid);
-                    pw.print(" active="); pw.print(app.active);
+                    pw.print(" uid=");
+                    pw.print(app.uid);
+                    pw.print(" active=");
+                    pw.print(app.active);
+
+                    if (!app.mFiles.isEmpty()) {
+                        shownGroups.add(app.mFiles.getFirst().groupName);
+                    }
                     pw.println();
+                    long bytesPinnedForApp = 0;
+                    long bytesPinnedForAppDeps = 0;
                     for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
                         pw.print("  ");
-                        pw.format("%s pinned:%d bytes (%d MB) pinlist:%b\n", pf.fileName,
+                        pw.format("%s pinned:%d bytes (%.2f MB) pinlist:%b\n", pf.fileName,
                                 pf.bytesPinned, pf.bytesPinned / bytesPerMB, pf.used_pinlist);
                         totalSize += pf.bytesPinned;
+                        bytesPinnedForApp += pf.bytesPinned;
                         shownPins.add(pf);
                         for (PinnedFile dep : pf.pinnedDeps) {
                             pw.print("  ");
-                            pw.format("%s pinned:%d bytes (%d MB) pinlist:%b (Dependency)\n", dep.fileName,
-                                    dep.bytesPinned, dep.bytesPinned / bytesPerMB, dep.used_pinlist);
+                            pw.format("%s pinned:%d bytes (%.2f MB) pinlist:%b (Dependency)\n",
+                                    dep.fileName, dep.bytesPinned, dep.bytesPinned / bytesPerMB,
+                                    dep.used_pinlist);
                             totalSize += dep.bytesPinned;
+                            bytesPinnedForAppDeps += dep.bytesPinned;
                             shownPins.add(dep);
                         }
                     }
+                    long bytesPinnedForAppAndDeps = bytesPinnedForApp + bytesPinnedForAppDeps;
+                    pw.format("Total Pinned = %d (%.2f MB) [App=%d (%.2f MB), "
+                                    + "Dependencies=%d (%.2f MB)]\n\n",
+                            bytesPinnedForAppAndDeps, bytesPinnedForAppAndDeps / bytesPerMB,
+                            bytesPinnedForApp, bytesPinnedForApp / bytesPerMB,
+                            bytesPinnedForAppDeps, bytesPinnedForAppDeps / bytesPerMB);
                 }
                 pw.println();
                 for (PinnedFile pinnedFile : mPinnedFiles.values()) {
-                    if (!groups.contains(pinnedFile.groupName)) {
-                        groups.add(pinnedFile.groupName);
+                    if (!groupsToPrint.contains(pinnedFile.groupName)
+                            && !shownGroups.contains(pinnedFile.groupName)) {
+                        groupsToPrint.add(pinnedFile.groupName);
                     }
                 }
-                boolean firstPinInGroup = true;
-                for (String group : groups) {
+
+                // Print all the non app groups.
+                for (String group : groupsToPrint) {
                     List<PinnedFile> groupPins = getAllPinsForGroup(group);
+                    pw.print("\nGroup:" + group);
+                    long bytesPinnedForGroupNoDeps = 0;
+                    long bytesPinnedForGroupDeps = 0;
+                    pw.println();
                     for (PinnedFile pinnedFile : groupPins) {
                         if (shownPins.contains(pinnedFile)) {
-                            // Already showed in the dump and accounted for, skip.
+                            // Already displayed and accounted for, skip.
                             continue;
                         }
-                        if (firstPinInGroup) {
-                            firstPinInGroup = false;
-                            // Ensure we only print when there are pins for groups not yet shown
-                            // in the pinned app section.
-                            pw.print("Group:" + group);
-                            pw.println();
-                        }
-                        pw.format("  %s pinned:%d bytes (%d MB) pinlist:%b\n", pinnedFile.fileName,
-                                pinnedFile.bytesPinned, pinnedFile.bytesPinned / bytesPerMB,
-                                pinnedFile.used_pinlist);
+                        pw.format("  %s pinned: %d bytes (%.2f MB) pinlist:%b\n",
+                                pinnedFile.fileName, pinnedFile.bytesPinned,
+                                pinnedFile.bytesPinned / bytesPerMB, pinnedFile.used_pinlist);
                         totalSize += pinnedFile.bytesPinned;
+                        bytesPinnedForGroupNoDeps += pinnedFile.bytesPinned;
+                        shownPins.add(pinnedFile);
+                        for (PinnedFile dep : pinnedFile.pinnedDeps) {
+                            if (shownPins.contains(dep)) {
+                                // Already displayed and accounted for, skip.
+                                continue;
+                            }
+                            pw.print("  ");
+                            pw.format("%s pinned:%d bytes (%.2f MB) pinlist:%b (Dependency)\n",
+                                    dep.fileName, dep.bytesPinned, dep.bytesPinned / bytesPerMB,
+                                    dep.used_pinlist);
+                            totalSize += dep.bytesPinned;
+                            bytesPinnedForGroupDeps += dep.bytesPinned;
+                            shownPins.add(dep);
+                        }
                     }
+                    long bytesPinnedForGroup = bytesPinnedForGroupNoDeps + bytesPinnedForGroupDeps;
+                    pw.format("Total Pinned = %d (%.2f MB) [Main=%d (%.2f MB), "
+                                    + "Dependencies=%d (%.2f MB)]\n\n",
+                            bytesPinnedForGroup, bytesPinnedForGroup / bytesPerMB,
+                            bytesPinnedForGroupNoDeps, bytesPinnedForGroupNoDeps / bytesPerMB,
+                            bytesPinnedForGroupDeps, bytesPinnedForGroupDeps / bytesPerMB);
                 }
                 pw.println();
                 if (mPinAnonAddress != 0) {
-                    pw.format("Pinned anon region: %d (%d MB)\n", mCurrentlyPinnedAnonSize, mCurrentlyPinnedAnonSize / bytesPerMB);
+                    pw.format("Pinned anon region: %d (%.2f MB)\n", mCurrentlyPinnedAnonSize,
+                            mCurrentlyPinnedAnonSize / bytesPerMB);
                     totalSize += mCurrentlyPinnedAnonSize;
                 }
-                pw.format("Total pinned: %s bytes (%s MB)\n", totalSize, totalSize / bytesPerMB);
+                pw.format("Total pinned: %d bytes (%.2f MB)\n", totalSize, totalSize / bytesPerMB);
+                pw.format("Available Pinner quota: %d bytes (%.2f MB)\n", getAvailableGlobalQuota(),
+                        getAvailableGlobalQuota() / bytesPerMB);
                 pw.println();
                 if (!mPendingRepin.isEmpty()) {
                     pw.print("Pending repin: ");
                     for (int key : mPendingRepin.values()) {
-                        pw.print(getNameForKey(key)); pw.print(' ');
+                        pw.print(getNameForKey(key));
+                        pw.print(' ');
                     }
                     pw.println();
                 }
@@ -1462,8 +1442,9 @@
                     repin();
                     break;
                 default:
-                    printError(out, String.format(
-                            "Unknown pinner command: %s. Supported commands: repin", command));
+                    printError(out,
+                            String.format("Unknown pinner command: %s. Supported commands: repin",
+                                    command));
                     resultReceiver.send(-1, null);
                     return;
             }
@@ -1479,46 +1460,6 @@
         }
     }
 
-    @VisibleForTesting
-    public static final class PinnedFile implements AutoCloseable {
-        private long mAddress;
-        final int mapSize;
-        final String fileName;
-        final int bytesPinned;
-
-        // Whether this file was pinned using a pinlist
-        boolean used_pinlist;
-
-        // User defined group name for pinner accounting
-        String groupName = "";
-        ArrayList<PinnedFile> pinnedDeps = new ArrayList<>();
-
-        PinnedFile(long address, int mapSize, String fileName, int bytesPinned) {
-             mAddress = address;
-             this.mapSize = mapSize;
-             this.fileName = fileName;
-             this.bytesPinned = bytesPinned;
-        }
-
-        @Override
-        public void close() {
-            if (mAddress >= 0) {
-                safeMunmap(mAddress, mapSize);
-                mAddress = -1;
-            }
-            for (PinnedFile dep : pinnedDeps) {
-                if (dep != null) {
-                    dep.close();
-                }
-            }
-        }
-
-        @Override
-        public void finalize() {
-            close();
-        }
-    }
-
     final static class PinRange {
         int start;
         int length;
@@ -1528,7 +1469,6 @@
      * Represents an app that was pinned.
      */
     private final class PinnedApp {
-
         /**
          * The uid of the package being pinned. This stays constant while the package stays
          * installed.
@@ -1557,11 +1497,9 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case PIN_ONSTART_MSG:
-                {
+                case PIN_ONSTART_MSG: {
                     handlePinOnStart();
-                }
-                break;
+                } break;
 
                 default:
                     super.handleMessage(msg);
diff --git a/services/core/java/com/android/server/pinner/PinnerUtils.java b/services/core/java/com/android/server/pinner/PinnerUtils.java
new file mode 100644
index 0000000..a836a83
--- /dev/null
+++ b/services/core/java/com/android/server/pinner/PinnerUtils.java
@@ -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.server.pinner;
+
+import android.annotation.Nullable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/* package */ final class PinnerUtils {
+    private static final String TAG = "PinnerUtils";
+
+    public static long clamp(long min, long value, long max) {
+        return Math.max(min, Math.min(value, max));
+    }
+
+    public static void safeMunmap(long address, long mapSize) {
+        try {
+            Os.munmap(address, mapSize);
+        } catch (ErrnoException ex) {
+            Slog.w(TAG, "ignoring error in unmap", ex);
+        }
+    }
+
+    /**
+     * Close FD, swallowing irrelevant errors.
+     */
+    public static void safeClose(@Nullable FileDescriptor fd) {
+        if (fd != null && fd.valid()) {
+            try {
+                Os.close(fd);
+            } catch (ErrnoException ex) {
+                // Swallow the exception: non-EBADF errors in close(2)
+                // indicate deferred paging write errors, which we
+                // don't care about here. The underlying file
+                // descriptor is always closed.
+                if (ex.errno == OsConstants.EBADF) {
+                    throw new AssertionError(ex);
+                }
+            }
+        }
+    }
+
+    /**
+     * Close closeable thing, swallowing errors.
+     */
+    public static void safeClose(@Nullable Closeable thing) {
+        if (thing != null) {
+            try {
+                thing.close();
+            } catch (IOException ex) {
+                Slog.w(TAG, "ignoring error closing resource: " + thing, ex);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/Android.bp b/services/core/java/com/android/server/pm/Android.bp
new file mode 100644
index 0000000..d625cf2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/Android.bp
@@ -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 {
+    default_team: "trendy_team_framework_android_packages",
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "framework-pm-service-sources",
+    srcs: [
+        "**/*.java",
+        "**/*.aidl",
+    ],
+    exclude_srcs: [
+        "dex/**/*.java",
+        "User*.java",
+        "Shortcut*.java",
+    ],
+    visibility: ["//frameworks/base"],
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index 5aea356..49a6ffd 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -191,6 +191,7 @@
     /**
      * Stop player proxy for the ongoing alarm and drop focus for its AudioFocusInfo.
      */
+    @SuppressLint("MissingPermission")
     @VisibleForTesting
     void muteAlarmSounds(Context context) {
         AudioManager audioManager = context.getSystemService(AudioManager.class);
@@ -201,6 +202,11 @@
                 }
             }
         }
+
+        AudioFocusInfo currentAfi = getAudioFocusInfoForNotification();
+        if (currentAfi != null) {
+            mFocusControlAudioPolicy.sendFocusLossAndUpdate(currentAfi);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 4665a72..89ced12 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2208,10 +2208,10 @@
             return true;
         }
         boolean permissionGranted = requireFullPermission ? hasPermission(
-                Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
                 : (hasPermission(
-                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                        || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS));
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
+                        || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, callingUid));
         if (!permissionGranted) {
             if (Process.isIsolatedUid(callingUid) && isKnownIsolatedComputeApp(callingUid)) {
                 return checkIsolatedOwnerHasPermission(callingUid, requireFullPermission);
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 9ecc7b9..1569fa0 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -70,13 +70,13 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
-import com.android.server.PinnerService;
 import com.android.server.art.ArtManagerLocal;
 import com.android.server.art.DexUseManagerLocal;
 import com.android.server.art.ReasonMapping;
 import com.android.server.art.model.ArtFlags;
 import com.android.server.art.model.DexoptParams;
 import com.android.server.art.model.DexoptResult;
+import com.android.server.pinner.PinnerService;
 import com.android.server.pm.PackageDexOptimizer.DexOptResult;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index f449126..aca65bf 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1019,7 +1019,9 @@
                     && scanInstallPackages(requests, createdAppId, versionInfos)) {
                 List<ReconciledPackage> reconciledPackages =
                         reconcileInstallPackages(requests, versionInfos);
-                if (reconciledPackages != null && commitInstallPackages(reconciledPackages)) {
+                if (reconciledPackages != null
+                        && renameAndUpdatePaths(requests)
+                        && commitInstallPackages(reconciledPackages)) {
                     success = true;
                 }
             }
@@ -1029,24 +1031,49 @@
         }
     }
 
-    private boolean prepareInstallPackages(List<InstallRequest> requests) {
-        // TODO: will remove the locking after doRename is moved out of prepare
+    private boolean renameAndUpdatePaths(List<InstallRequest> requests) {
         try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
             for (InstallRequest request : requests) {
-                try {
-                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
-                    request.onPrepareStarted();
-                    preparePackageLI(request);
-                } catch (PrepareFailure prepareFailure) {
-                    request.setError(prepareFailure.error,
-                            prepareFailure.getMessage());
-                    request.setOriginPackage(prepareFailure.mConflictingPackage);
-                    request.setOriginPermission(prepareFailure.mConflictingPermission);
-                    return false;
-                } finally {
-                    request.onPrepareFinished();
-                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                ParsedPackage parsedPackage = request.getParsedPackage();
+                final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0;
+                if (isApex) {
+                    continue;
                 }
+                try {
+                    doRenameLI(request, parsedPackage);
+                    setUpFsVerity(parsedPackage);
+                } catch (Installer.InstallerException | IOException | DigestException
+                         | NoSuchAlgorithmException | PrepareFailure e) {
+                    request.setError(PackageManagerException.INTERNAL_ERROR_VERITY_SETUP,
+                            "Failed to set up verity: " + e);
+                    return false;
+                }
+
+                // update paths that are set before renaming
+                PackageSetting scannedPackageSetting = request.getScannedPackageSetting();
+                scannedPackageSetting.setPath(new File(parsedPackage.getPath()));
+                scannedPackageSetting.setLegacyNativeLibraryPath(
+                        parsedPackage.getNativeLibraryRootDir());
+            }
+            return true;
+        }
+    }
+
+    private boolean prepareInstallPackages(List<InstallRequest> requests) {
+        for (InstallRequest request : requests) {
+            try {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
+                request.onPrepareStarted();
+                preparePackage(request);
+            } catch (PrepareFailure prepareFailure) {
+                request.setError(prepareFailure.error,
+                        prepareFailure.getMessage());
+                request.setOriginPackage(prepareFailure.mConflictingPackage);
+                request.setOriginPermission(prepareFailure.mConflictingPermission);
+                return false;
+            } finally {
+                request.onPrepareFinished();
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
         }
         return true;
@@ -1231,8 +1258,7 @@
         return newProp != null && newProp.getBoolean();
     }
 
-    @GuardedBy("mPm.mInstallLock")
-    private void preparePackageLI(InstallRequest request) throws PrepareFailure {
+    private void preparePackage(InstallRequest request) throws PrepareFailure {
         final int[] allUsers =  mPm.mUserManager.getUserIds();
         final int installFlags = request.getInstallFlags();
         final boolean onExternal = request.getVolumeUuid() != null;
@@ -1739,18 +1765,7 @@
             }
         }
 
-        if (!isApex) {
-            doRenameLI(request, parsedPackage);
-
-            try {
-                setUpFsVerity(parsedPackage);
-            } catch (Installer.InstallerException | IOException | DigestException
-                    | NoSuchAlgorithmException e) {
-                throw PrepareFailure.ofInternalError(
-                        "Failed to set up verity: " + e,
-                        PackageManagerException.INTERNAL_ERROR_VERITY_SETUP);
-            }
-        } else {
+        if (isApex) {
             // Use the path returned by apexd
             parsedPackage.setPath(request.getApexInfo().modulePath);
             parsedPackage.setBaseApkPath(request.getApexInfo().modulePath);
@@ -2092,7 +2107,21 @@
 
         // Reflect the rename in scanned details
         try {
-            parsedPackage.setPath(afterCodeFile.getCanonicalPath());
+            String afterCanonicalPath = afterCodeFile.getCanonicalPath();
+            String beforeCanonicalPath = beforeCodeFile.getCanonicalPath();
+            parsedPackage.setPath(afterCanonicalPath);
+
+            parsedPackage.setNativeLibraryDir(
+                    parsedPackage.getNativeLibraryDir()
+                            .replace(beforeCanonicalPath, afterCanonicalPath));
+            parsedPackage.setNativeLibraryRootDir(
+                    parsedPackage.getNativeLibraryRootDir()
+                            .replace(beforeCanonicalPath, afterCanonicalPath));
+            String secondaryNativeLibraryDir = parsedPackage.getSecondaryNativeLibraryDir();
+            if (secondaryNativeLibraryDir != null) {
+                parsedPackage.setSecondaryNativeLibraryDir(
+                        secondaryNativeLibraryDir.replace(beforeCanonicalPath, afterCanonicalPath));
+            }
         } catch (IOException e) {
             Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
             throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE,
@@ -2102,6 +2131,7 @@
                 afterCodeFile, parsedPackage.getBaseApkPath()));
         parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
                 afterCodeFile, parsedPackage.getSplitCodePaths()));
+        request.updateAllCodePaths(AndroidPackageUtils.getAllCodePaths(parsedPackage));
     }
 
     // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index ae7749b..1f79ac0 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -615,6 +615,20 @@
         return mScanResult.mDynamicSharedLibraryInfos;
     }
 
+    public void updateAllCodePaths(List<String> paths) {
+        if (mScanResult.mSdkSharedLibraryInfo != null) {
+            mScanResult.mSdkSharedLibraryInfo.setAllCodePaths(paths);
+        }
+        if (mScanResult.mStaticSharedLibraryInfo != null) {
+            mScanResult.mStaticSharedLibraryInfo.setAllCodePaths(paths);
+        }
+        if (mScanResult.mDynamicSharedLibraryInfos != null) {
+            for (SharedLibraryInfo info : mScanResult.mDynamicSharedLibraryInfos) {
+                info.setAllCodePaths(paths);
+            }
+        }
+    }
+
     @Nullable
     public PackageSetting getScannedPackageSetting() {
         assertScanResultExists();
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index efd58ed..8657de2 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -716,7 +716,7 @@
                     visiblePackages.add(info.getActivityInfo().packageName);
                 }
                 final List<ApplicationInfo> installedPackages =
-                        mPackageManagerInternal.getInstalledApplications(
+                        mPackageManagerInternal.getInstalledApplicationsCrossUser(
                                 /* flags= */ 0, user.getIdentifier(), callingUid);
                 for (ApplicationInfo applicationInfo : installedPackages) {
                     if (!visiblePackages.contains(applicationInfo.packageName)) {
@@ -1674,18 +1674,32 @@
         @Override
         public PendingIntent getActivityLaunchIntent(String callingPackage, ComponentName component,
                 UserHandle user) {
+            try {
+                Log.d(TAG,
+                        "getActivityLaunchIntent callingPackage=" + callingPackage + " component="
+                                + component + " user=" + user);
+            } catch (Exception e) {
+                Log.e(TAG, "getActivityLaunchIntent is called and error occurred when"
+                        + " printing the logs", e);
+            }
             if (mContext.checkPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS,
                     injectBinderCallingPid(), injectBinderCallingUid())
                             != PackageManager.PERMISSION_GRANTED) {
+                Log.d(TAG, "getActivityLaunchIntent no permission callingPid="
+                        + injectBinderCallingPid() + " callingUid=" + injectBinderCallingUid());
                 throw new SecurityException("Permission START_TASKS_FROM_RECENTS required");
             }
             if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) {
+                Log.d(TAG, "getActivityLaunchIntent cannot access profile user="
+                        + user.getIdentifier());
                 throw new ActivityNotFoundException("Activity could not be found");
             }
 
             final Intent launchIntent = getMainActivityLaunchIntent(component, user,
                     false /* includeArchivedApps */);
             if (launchIntent == null) {
+                Log.d(TAG, "getActivityLaunchIntent cannot access profile user="
+                        + user.getIdentifier() + " component=" + component);
                 throw new SecurityException("Attempt to launch activity without "
                         + " category Intent.CATEGORY_LAUNCHER " + component);
             }
@@ -1965,6 +1979,17 @@
                     canLaunch = true;
                 }
                 if (!canLaunch) {
+                    try {
+                        Log.w(TAG, "getMainActivityLaunchIntent return null because it can't launch"
+                                + " component=" + component + " user=" + user + " appsSize=" + size
+                                + " includeArchivedApps=" + includeArchivedApps
+                                + " isArchivingEnabled=" + isArchivingEnabled()
+                                + " matchingArchivedAppActivityInfo="
+                                + getMatchingArchivedAppActivityInfo(component, user));
+                    } catch (Exception e) {
+                        Log.e(TAG, "getMainActivityLaunchIntent return null and error occurred when"
+                                + " printing the logs", e);
+                    }
                     return null;
                 }
             } finally {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1316df1..b1b1637 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -50,6 +50,7 @@
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -201,6 +202,9 @@
             Manifest.permission.USE_FULL_SCREEN_INTENT
     );
 
+    private static final String ROLE_SYSTEM_APP_PROTECTION_SERVICE =
+            "android.app.role.SYSTEM_APP_PROTECTION_SERVICE";
+
     final PackageArchiver mPackageArchiver;
 
     private final Context mContext;
@@ -1454,6 +1458,12 @@
                     .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE)
                     .setAdmin(callerPackageName)
                     .write();
+        } else if (isSystemAppProtectionRoleHolder(snapshot, userId, callingUid)) {
+            // Allow the SYSTEM_APP_PROTECTION_SERVICE role holder to silently uninstall, with a
+            // clean calling identity to get DELETE_PACKAGES permission
+            Binder.withCleanCallingIdentity(() ->
+                    mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags)
+            );
         } else {
             ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId);
             if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
@@ -1475,6 +1485,29 @@
         }
     }
 
+    private Boolean isSystemAppProtectionRoleHolder(
+            @NonNull Computer snapshot, int userId, int callingUid) {
+        if (!Flags.deletePackagesSilentlyBackport()) {
+            return false;
+        }
+        String holderPackageName = Binder.withCleanCallingIdentity(() -> {
+            RoleManager roleManager = mPm.mContext.getSystemService(RoleManager.class);
+            if (roleManager == null) {
+                return null;
+            }
+            List<String> holders = roleManager.getRoleHoldersAsUser(
+                    ROLE_SYSTEM_APP_PROTECTION_SERVICE, UserHandle.of(userId));
+            if (holders.isEmpty()) {
+                return null;
+            }
+            return holders.get(0);
+        });
+        if (holderPackageName == null) {
+            return false;
+        }
+        return snapshot.getPackageUid(holderPackageName, /* flags= */ 0, userId) == callingUid;
+    }
+
     @Override
     public void uninstallExistingPackage(VersionedPackage versionedPackage,
             String callerPackageName, IntentSender statusReceiver, int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ff9c3e5..611e0d8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3389,18 +3389,31 @@
                     return true;
                 }
                 // Does it contain a device admin for any user?
-                int[] users;
+                int[] allUsers = mUserManager.getUserIds();
+                int[] targetUsers;
                 if (userId == UserHandle.USER_ALL) {
-                    users = mUserManager.getUserIds();
+                    targetUsers = allUsers;
                 } else {
-                    users = new int[]{userId};
+                    targetUsers = new int[]{userId};
                 }
-                for (int i = 0; i < users.length; ++i) {
-                    if (dpm.packageHasActiveAdmins(packageName, users[i])) {
+
+                for (int i = 0; i < targetUsers.length; ++i) {
+                    if (dpm.packageHasActiveAdmins(packageName, targetUsers[i])) {
                         return true;
                     }
-                    if (isDeviceManagementRoleHolder(packageName, users[i])
-                            && dpmi.isUserOrganizationManaged(users[i])) {
+                }
+
+                // If a package is DMRH on a managed user, it should also be treated as an admin on
+                // that user. If that package is also a system package, it should also be protected
+                // on other users otherwise "uninstall updates" on an unmanaged user may break
+                // management on other users because apk version is shared between all users.
+                var packageState = snapshotComputer().getPackageStateInternal(packageName);
+                if (packageState == null) {
+                    return false;
+                }
+                for (int user : packageState.isSystem() ? allUsers : targetUsers) {
+                    if (isDeviceManagementRoleHolder(packageName, user)
+                            && dpmi.isUserOrganizationManaged(user)) {
                         return true;
                     }
                 }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 60056eb..6f50295 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -912,12 +912,13 @@
      * available ShareTarget definitions in this package.
      */
     public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
-            @NonNull final IntentFilter filter) {
-        return getMatchingShareTargets(filter, null);
+            @NonNull final IntentFilter filter, final int callingUserId) {
+        return getMatchingShareTargets(filter, null, callingUserId);
     }
 
     List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
-            @NonNull final IntentFilter filter, @Nullable final String pkgName) {
+            @NonNull final IntentFilter filter, @Nullable final String pkgName,
+            final int callingUserId) {
         synchronized (mPackageItemLock) {
             final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
             for (int i = 0; i < mShareTargets.size(); i++) {
@@ -941,7 +942,7 @@
             // included in the result
             findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
                     ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
-                    pkgName, 0, /*getPinnedByAnyLauncher=*/ false);
+                    pkgName, callingUserId, /*getPinnedByAnyLauncher=*/ false);
 
             final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
             for (int i = 0; i < shortcuts.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a3ff195..5518bfa 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2591,7 +2591,8 @@
             final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
             final ShortcutUser user = getUserShortcutsLocked(userId);
             user.forAllPackages(p -> shortcutInfoList.addAll(
-                    p.getMatchingShareTargets(filter, pkg)));
+                    p.getMatchingShareTargets(filter, pkg,
+                            mUserManagerInternal.getProfileParentId(userId))));
             return new ParceledListSlice<>(shortcutInfoList);
         }
     }
@@ -2623,7 +2624,8 @@
 
             final List<ShortcutManager.ShareShortcutInfo> matchedTargets =
                     getPackageShortcutsLocked(packageName, userId)
-                            .getMatchingShareTargets(filter);
+                            .getMatchingShareTargets(filter,
+                                    mUserManagerInternal.getProfileParentId(callingUserId));
             final int matchedSize = matchedTargets.size();
             for (int i = 0; i < matchedSize; i++) {
                 if (matchedTargets.get(i).getShortcutInfo().getId().equals(shortcutId)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 89417f3..8bab9de 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1101,8 +1101,6 @@
         if (android.multiuser.Flags.cachesNotInvalidatedAtStartReadOnly()) {
             UserManager.invalidateIsUserUnlockedCache();
             UserManager.invalidateQuietModeEnabledCache();
-            UserManager.invalidateStaticUserProperties();
-            UserManager.invalidateUserPropertiesCache();
             UserManager.invalidateUserSerialNumberCache();
         }
     }
@@ -2647,11 +2645,15 @@
     }
 
     @Override
-    public int getMainDisplayIdAssignedToUser() {
-        // Not checking for any permission as it returns info about calling user
-        int userId = UserHandle.getUserId(Binder.getCallingUid());
-        int displayId = mUserVisibilityMediator.getMainDisplayAssignedToUser(userId);
-        return displayId;
+    public int getMainDisplayIdAssignedToUser(int userId) {
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (callingUserId != userId
+                && !hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+            throw new SecurityException("Caller from user " + callingUserId + " needs MANAGE_USERS "
+                    + "or INTERACT_ACROSS_USERS permission to get the main display for (" + userId
+                    + ")");
+        }
+        return mUserVisibilityMediator.getMainDisplayAssignedToUser(userId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index 46207c1..b43ddaa 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -572,7 +572,7 @@
                 return false;
             }
 
-            // First check if the user started on display
+            // First check if the user is assigned to a display
             int userAssignedToDisplay = getUserStartedOnDisplay(displayId);
             if (userAssignedToDisplay != USER_NULL) {
                 Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because display was assigned"
@@ -918,10 +918,16 @@
                 if (!isStartedVisibleProfileLocked(userId)) {
                     return userId;
                 } else if (DBG) {
-                    Slogf.d(TAG, "getUserAssignedToDisplay(%d): skipping user %d because it's "
-                            + "a profile", displayId, userId);
+                    Slogf.d(TAG,
+                            "getUserAssignedToDisplay(%d): skipping user %d because it's a profile",
+                            displayId, userId);
                 }
             }
+            int userAssignedToExtraDisplay = mExtraDisplaysAssignedToUsers.get(displayId,
+                    USER_NULL);
+            if (userAssignedToExtraDisplay != USER_NULL) {
+                return userAssignedToExtraDisplay;
+            }
         }
         if (!returnCurrentUserByDefault) {
             if (DBG) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index df9f7fb..5fc3e33 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1015,8 +1015,7 @@
                     permission, attributionSource, message, forDataDelivery, startDataDelivery,
                     fromDatasource, attributedOp);
             // Finish any started op if some step in the attribution chain failed.
-            if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED
-                    && result != PermissionChecker.PERMISSION_SOFT_DENIED) {
+            if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) {
                 if (attributedOp == AppOpsManager.OP_NONE) {
                     finishDataDelivery(AppOpsManager.permissionToOpCode(permission),
                             attributionSource.asState(), fromDatasource);
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 9dfaca8..1592ef3 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -65,21 +65,21 @@
      *       };
      *  </pre>
      */
-    abstract static class TwoKeysCombinationRule {
+    public abstract static class TwoKeysCombinationRule {
         private int mKeyCode1;
         private int mKeyCode2;
 
-        TwoKeysCombinationRule(int keyCode1, int keyCode2) {
+        public TwoKeysCombinationRule(int keyCode1, int keyCode2) {
             mKeyCode1 = keyCode1;
             mKeyCode2 = keyCode2;
         }
 
-        boolean preCondition() {
+        public boolean preCondition() {
             return true;
         }
 
         boolean shouldInterceptKey(int keyCode) {
-            return preCondition() && (keyCode == mKeyCode1 || keyCode == mKeyCode2);
+            return (keyCode == mKeyCode1 || keyCode == mKeyCode2) && preCondition();
         }
 
         boolean shouldInterceptKeys(SparseLongArray downTimes) {
@@ -94,12 +94,12 @@
         }
 
         // The excessive delay before it dispatching to client.
-        long getKeyInterceptDelayMs() {
+        public long getKeyInterceptDelayMs() {
             return COMBINE_KEY_DELAY_MILLIS;
         }
 
-        abstract void execute();
-        abstract void cancel();
+        public abstract void execute();
+        public abstract void cancel();
 
         @Override
         public String toString() {
@@ -128,18 +128,18 @@
         }
     }
 
-    KeyCombinationManager(Handler handler) {
+    public KeyCombinationManager(Handler handler) {
         mHandler = handler;
     }
 
-    void addRule(TwoKeysCombinationRule rule) {
+    public void addRule(TwoKeysCombinationRule rule) {
         if (mRules.contains(rule)) {
             throw new IllegalArgumentException("Rule : " + rule + " already exists.");
         }
         mRules.add(rule);
     }
 
-    void removeRule(TwoKeysCombinationRule rule) {
+    public void removeRule(TwoKeysCombinationRule rule) {
         mRules.remove(rule);
     }
 
@@ -148,7 +148,7 @@
      * to a window.
      * Return true if any active rule could be triggered by the key event, otherwise false.
      */
-    boolean interceptKey(KeyEvent event, boolean interactive) {
+    public boolean interceptKey(KeyEvent event, boolean interactive) {
         synchronized (mLock) {
             return interceptKeyLocked(event, interactive);
         }
@@ -226,7 +226,7 @@
     /**
      * Return the interceptTimeout to tell InputDispatcher when is ready to deliver to window.
      */
-    long getKeyInterceptTimeout(int keyCode) {
+    public long getKeyInterceptTimeout(int keyCode) {
         synchronized (mLock) {
             if (mDownTimes.get(keyCode) == 0) {
                 return 0;
@@ -246,7 +246,7 @@
     /**
      * True if the key event had been handled.
      */
-    boolean isKeyConsumed(KeyEvent event) {
+    public boolean isKeyConsumed(KeyEvent event) {
         synchronized (mLock) {
             if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
                 return false;
@@ -258,7 +258,7 @@
     /**
      * True if power key is the candidate.
      */
-    boolean isPowerKeyIntercepted() {
+    public boolean isPowerKeyIntercepted() {
         synchronized (mLock) {
             if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
                 // return false if only if power key pressed.
@@ -294,7 +294,7 @@
         return false;
     }
 
-    void dump(String prefix, PrintWriter pw) {
+    public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "KeyCombination rules:");
         forAllRules(mRules, (rule)-> {
             pw.println(prefix + "  " + rule);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 02c02b0..2284050 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -37,6 +37,7 @@
 import static android.os.Build.VERSION_CODES.M;
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
+import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
@@ -47,6 +48,7 @@
 import static android.view.KeyEvent.KEYCODE_HOME;
 import static android.view.KeyEvent.KEYCODE_POWER;
 import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
+import static android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL;
 import static android.view.KeyEvent.KEYCODE_UNKNOWN;
 import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
 import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
@@ -84,6 +86,7 @@
 import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable;
 import static com.android.hardware.input.Flags.modifierShortcutDump;
 import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures;
 import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
@@ -183,6 +186,7 @@
 import android.service.vr.IPersistentVrStateCallbacks;
 import android.speech.RecognizerIntent;
 import android.telecom.TelecomManager;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.MutableBoolean;
@@ -195,7 +199,6 @@
 import android.view.IDisplayFoldListener;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
-import android.view.KeyCharacterMap.FallbackAction;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.MotionEvent;
@@ -255,6 +258,7 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -694,10 +698,6 @@
     // Maps global key codes to the components that will handle them.
     private GlobalKeyManager mGlobalKeyManager;
 
-    // Fallback actions by key code.
-    private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions =
-            new SparseArray<KeyCharacterMap.FallbackAction>();
-
     private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator
             = new LogDecelerateInterpolator(100, 0);
     private final DeferredKeyActionExecutor mDeferredKeyActionExecutor =
@@ -717,6 +717,22 @@
     // Timeout for showing the keyguard after the screen is on, in case no "ready" is received.
     private int mKeyguardDrawnTimeout = 1000;
 
+    private final boolean mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();
+
+    // Key codes that should be ignored for visible background users in MUMD environment.
+    private static final Set<Integer> KEY_CODES_IGNORED_FOR_VISIBLE_BACKGROUND_USERS =
+            new ArraySet<>(Arrays.asList(
+                    KeyEvent.KEYCODE_POWER,
+                    KeyEvent.KEYCODE_SLEEP,
+                    KeyEvent.KEYCODE_WAKEUP,
+                    KeyEvent.KEYCODE_CALL,
+                    KeyEvent.KEYCODE_ENDCALL,
+                    KeyEvent.KEYCODE_ASSIST,
+                    KeyEvent.KEYCODE_VOICE_ASSIST,
+                    KeyEvent.KEYCODE_MUTE,
+                    KeyEvent.KEYCODE_VOLUME_MUTE
+            ));
+
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
     private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
     private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
@@ -810,7 +826,11 @@
                     event.recycle();
                     break;
                 case MSG_HANDLE_ALL_APPS:
-                    launchAllAppsAction();
+                    KeyEvent keyEvent = (KeyEvent) msg.obj;
+                    if (isKeyEventForCurrentUser(keyEvent.getDisplayId(), keyEvent.getKeyCode(),
+                            "launchAllAppsViaA11y")) {
+                        launchAllAppsAction();
+                    }
                     break;
                 case MSG_RINGER_TOGGLE_CHORD:
                     handleRingerChordGesture();
@@ -1032,7 +1052,8 @@
         return handled;
     }
 
-    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
+    private void interceptPowerKeyDown(KeyEvent event, boolean interactive,
+            boolean isKeyGestureTriggered) {
         // Hold a wake lock until the power key is released.
         if (!mPowerKeyWakeLock.isHeld()) {
             mPowerKeyWakeLock.acquire();
@@ -1065,7 +1086,8 @@
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
         mPowerKeyHandled = mPowerKeyHandled || hungUp
-                || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
+                || handledByPowerManager || isKeyGestureTriggered
+                || mKeyCombinationManager.isPowerKeyIntercepted();
         if (!mPowerKeyHandled) {
             if (!interactive) {
                 wakeUpFromWakeKey(event);
@@ -2081,7 +2103,10 @@
             switch (mLongPressOnHomeBehavior) {
                 case LONG_PRESS_HOME_ALL_APPS:
                     notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
-                    launchAllAppsAction();
+                    if (isKeyEventForCurrentUser(event.getDisplayId(), event.getKeyCode(),
+                            "launchAllAppsViaA11y")) {
+                        launchAllAppsAction();
+                    }
                     break;
                 case LONG_PRESS_HOME_ASSIST:
                     notifyKeyGestureCompleted(event,
@@ -2438,6 +2463,9 @@
 
     private void initKeyCombinationRules() {
         mKeyCombinationManager = new KeyCombinationManager(mHandler);
+        if (useKeyGestureEventHandler() && useKeyGestureEventHandlerMultiPressGestures()) {
+            return;
+        }
         final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableScreenshotChord);
 
@@ -2445,13 +2473,13 @@
             mKeyCombinationManager.addRule(
                     new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
                         @Override
-                        void execute() {
+                        public void execute() {
                             mPowerKeyHandled = true;
                             interceptScreenshotChord(
                                     SCREENSHOT_KEY_CHORD, getScreenshotChordLongPressDelay());
                         }
                         @Override
-                        void cancel() {
+                        public void cancel() {
                             cancelPendingScreenshotChordAction();
                         }
                     });
@@ -2460,13 +2488,13 @@
                 mKeyCombinationManager.addRule(
                         new TwoKeysCombinationRule(KEYCODE_POWER, KEYCODE_STEM_PRIMARY) {
                             @Override
-                            void execute() {
+                            public void execute() {
                                 mPowerKeyHandled = true;
                                 interceptScreenshotChord(SCREENSHOT_KEY_CHORD,
                                         getScreenshotChordLongPressDelay());
                             }
                             @Override
-                            void cancel() {
+                            public void cancel() {
                                 cancelPendingScreenshotChordAction();
                             }
                         });
@@ -2476,16 +2504,16 @@
         mKeyCombinationManager.addRule(
                 new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP) {
                     @Override
-                    boolean preCondition() {
+                    public boolean preCondition() {
                         return mAccessibilityShortcutController
                                 .isAccessibilityShortcutAvailable(isKeyguardLocked());
                     }
                     @Override
-                    void execute() {
+                    public void execute() {
                         interceptAccessibilityShortcutChord();
                     }
                     @Override
-                    void cancel() {
+                    public void cancel() {
                         cancelPendingAccessibilityShortcutAction();
                     }
                 });
@@ -2496,7 +2524,7 @@
         mKeyCombinationManager.addRule(
                 new TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) {
                     @Override
-                    boolean preCondition() {
+                    public boolean preCondition() {
                         switch (mPowerVolUpBehavior) {
                             case POWER_VOLUME_UP_BEHAVIOR_MUTE:
                                 return mRingerToggleChord != VOLUME_HUSH_OFF;
@@ -2505,7 +2533,7 @@
                         }
                     }
                     @Override
-                    void execute() {
+                    public void execute() {
                         switch (mPowerVolUpBehavior) {
                             case POWER_VOLUME_UP_BEHAVIOR_MUTE:
                                 // no haptic feedback here since
@@ -2524,7 +2552,7 @@
                         }
                     }
                     @Override
-                    void cancel() {
+                    public void cancel() {
                         switch (mPowerVolUpBehavior) {
                             case POWER_VOLUME_UP_BEHAVIOR_MUTE:
                                 cancelPendingRingerToggleChordAction();
@@ -2540,16 +2568,16 @@
             mKeyCombinationManager.addRule(
                     new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
                         @Override
-                        void execute() {
+                        public void execute() {
                             mBackKeyHandled = true;
                             interceptAccessibilityGestureTv();
                         }
                         @Override
-                        void cancel() {
+                        public void cancel() {
                             cancelAccessibilityGestureTv();
                         }
                         @Override
-                        long getKeyInterceptDelayMs() {
+                        public long getKeyInterceptDelayMs() {
                             // Use a timeout of 0 to prevent additional latency in processing of
                             // this key. This will potentially cause some unwanted UI actions if the
                             // user does end up triggering the key combination later, but in most
@@ -2563,16 +2591,16 @@
             mKeyCombinationManager.addRule(
                     new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
                         @Override
-                        void execute() {
+                        public void execute() {
                             mBackKeyHandled = true;
                             interceptBugreportGestureTv();
                         }
                         @Override
-                        void cancel() {
+                        public void cancel() {
                             cancelBugreportGestureTv();
                         }
                         @Override
-                        long getKeyInterceptDelayMs() {
+                        public long getKeyInterceptDelayMs() {
                             return 0;
                         }
                     });
@@ -2643,7 +2671,7 @@
         }
 
         @Override
-        void onKeyUp(long eventTime, int count, int displayId) {
+        void onKeyUp(long eventTime, int count, int displayId, int deviceId, int metaState) {
             if (mShouldEarlyShortPressOnPower && count == 1) {
                 powerPress(eventTime, 1 /*pressCount*/, displayId);
             }
@@ -2763,7 +2791,7 @@
         }
 
         @Override
-        void onKeyUp(long eventTime, int count, int unusedDisplayId) {
+        void onKeyUp(long eventTime, int count, int displayId, int deviceId, int metaState) {
             if (count == 1) {
                 // Save info about the most recent task on the first press of the stem key. This
                 // may be used later to switch to the most recent app using double press gesture.
@@ -2816,6 +2844,33 @@
         }
     }
 
+    // TODO(b/358569822): Move to KeyGestureController.
+    private final class StylusTailButtonRule extends SingleKeyGestureDetector.SingleKeyRule {
+        StylusTailButtonRule() {
+            super(KEYCODE_STYLUS_BUTTON_TAIL);
+        }
+
+        @Override
+        int getMaxMultiPressCount() {
+            return 2;
+        }
+
+        @Override
+        void onPress(long downTime, int displayId) {
+
+        }
+
+        @Override
+        void onKeyUp(long eventTime, int pressCount, int displayId, int deviceId, int metaState) {
+            if (pressCount != 1) {
+                return;
+            }
+            // Single press on tail button triggers the open notes gesture.
+            handleKeyGestureInKeyGestureController(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES,
+                    deviceId, KEYCODE_STYLUS_BUTTON_TAIL, metaState);
+        }
+    }
+
     private void initSingleKeyGestureRules(Looper looper) {
         mSingleKeyGestureDetector = SingleKeyGestureDetector.get(mContext, looper);
         mSingleKeyGestureDetector.addRule(new PowerKeyRule());
@@ -2825,6 +2880,7 @@
         if (hasStemPrimaryBehavior()) {
             mSingleKeyGestureDetector.addRule(new StemPrimaryKeyRule());
         }
+        mSingleKeyGestureDetector.addRule(new StylusTailButtonRule());
     }
 
     /**
@@ -3314,6 +3370,16 @@
                 new int[]{event.getKeyCode()}, event.getMetaState(), gestureType);
     }
 
+    private void handleKeyGestureInKeyGestureController(
+            @KeyGestureEvent.KeyGestureType int gestureType, int deviceId, int keyCode,
+            int metaState) {
+        if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
+            return;
+        }
+        mInputManagerInternal.handleKeyGestureInKeyGestureController(deviceId, new int[]{keyCode},
+                metaState, gestureType);
+    }
+
     @Override
     public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
         return mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(deviceId);
@@ -3338,15 +3404,18 @@
                             + keyguardOn() + " canceled=" + event.isCanceled());
         }
 
-        if (mKeyCombinationManager.isKeyConsumed(event)) {
-            return keyConsumed;
-        }
+        if (!useKeyGestureEventHandler()) {
+            if (mKeyCombinationManager.isKeyConsumed(event)) {
+                return keyConsumed;
+            }
 
-        if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
-            final long now = SystemClock.uptimeMillis();
-            final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode);
-            if (now < interceptTimeout) {
-                return interceptTimeout - now;
+            if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
+                final long now = SystemClock.uptimeMillis();
+                final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(
+                        keyCode);
+                if (now < interceptTimeout) {
+                    return interceptTimeout - now;
+                }
             }
         }
 
@@ -3388,8 +3457,16 @@
     // NOTE: Please try not to add new Shortcut combinations here and instead use KeyGestureEvents.
     // Add shortcut trigger logic in {@code KeyGestureController} and add handling logic in
     // {@link handleKeyGesture()}
-    @SuppressLint("MissingPermission")
     private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
+        if (useKeyGestureEventHandler()) {
+            return interceptSystemKeysAndShortcutsNew(focusedToken, event);
+        } else {
+            return interceptSystemKeysAndShortcutsOld(focusedToken, event);
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    private boolean interceptSystemKeysAndShortcutsOld(IBinder focusedToken, KeyEvent event) {
         final boolean keyguardOn = keyguardOn();
         final int keyCode = event.getKeyCode();
         final int repeatCount = event.getRepeatCount();
@@ -3724,7 +3801,10 @@
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
                     } else if (mPendingMetaAction) {
                         if (!canceled) {
-                            launchAllAppsAction();
+                            if (isKeyEventForCurrentUser(event.getDisplayId(), event.getKeyCode(),
+                                    "launchAllAppsViaA11y")) {
+                                launchAllAppsAction();
+                            }
                             notifyKeyGestureCompleted(event,
                                     KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
                         }
@@ -3810,6 +3890,59 @@
         return (metaState & KeyEvent.META_META_ON) != 0;
     }
 
+    private boolean interceptSystemKeysAndShortcutsNew(IBinder focusedToken, KeyEvent event) {
+        final int keyCode = event.getKeyCode();
+        final int metaState = event.getMetaState();
+        final boolean keyguardOn = keyguardOn();
+
+        if (isUserSetupComplete() && !keyguardOn) {
+            if (mModifierShortcutManager.interceptKey(event)) {
+                dismissKeyboardShortcutsMenu();
+                return true;
+            }
+        }
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_HOME:
+                return handleHomeShortcuts(focusedToken, event);
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_MUTE:
+                if (mUseTvRouting || mHandleVolumeKeysInWM) {
+                    // On TVs or when the configuration is enabled, volume keys never
+                    // go to the foreground app.
+                    dispatchDirectAudioEvent(event);
+                    return true;
+                }
+
+                // If the device is in VR mode and keys are "internal" (e.g. on the side of the
+                // device), then drop the volume keys and don't forward it to the
+                // application/dispatch the audio event.
+                if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
+                    final InputDevice d = event.getDevice();
+                    if (d != null && !d.isExternal()) {
+                        return true;
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_STEM_PRIMARY:
+                if (prepareToSendSystemKeyToApplication(focusedToken, event)) {
+                    // Send to app.
+                    return false;
+                } else {
+                    // Intercepted.
+                    sendSystemKeyToStatusBarAsync(event);
+                    return true;
+                }
+        }
+        if (isValidGlobalKey(keyCode)
+                && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
+            return true;
+        }
+
+        // Reserve all the META modifier combos for system behavior
+        return (metaState & KeyEvent.META_META_ON) != 0;
+    }
+
     @SuppressLint("MissingPermission")
     private void initKeyGestures() {
         if (!useKeyGestureEventHandler()) {
@@ -3819,7 +3952,13 @@
             @Override
             public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
                     @Nullable IBinder focusedToken) {
-                return PhoneWindowManager.this.handleKeyGestureEvent(event, focusedToken);
+                boolean handled = PhoneWindowManager.this.handleKeyGestureEvent(event,
+                        focusedToken);
+                if (handled && Arrays.stream(event.getKeycodes()).anyMatch(
+                        (keycode) -> keycode == KeyEvent.KEYCODE_POWER)) {
+                    mPowerKeyHandled = true;
+                }
+                return handled;
             }
 
             @Override
@@ -3849,7 +3988,20 @@
                     case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
                     case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
                         return true;
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
+                        return mDefaultDisplayPolicy.isAwake();
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
+                        return mDefaultDisplayPolicy.isAwake() && mAccessibilityShortcutController
+                                .isAccessibilityShortcutAvailable(isKeyguardLocked());
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
+                        return mDefaultDisplayPolicy.isAwake() && mAccessibilityShortcutController
+                                .isAccessibilityShortcutAvailable(false);
                     default:
                         return false;
                 }
@@ -3991,7 +4143,8 @@
                 return true;
             case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
             case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
-                if (complete) {
+                if (complete && isKeyEventForCurrentUser(event.getDisplayId(),
+                        event.getKeycodes()[0], "launchAllAppsViaA11y")) {
                     launchAllAppsAction();
                 }
                 return true;
@@ -4006,6 +4159,66 @@
                     sendSwitchKeyboardLayout(displayId, focusedToken, direction);
                 }
                 return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
+                if (start) {
+                    // Screenshot chord is pressed: Wait for long press delay before taking
+                    // screenshot
+                    interceptScreenshotChord(SCREENSHOT_KEY_CHORD,
+                            getScreenshotChordLongPressDelay());
+                } else {
+                    cancelPendingScreenshotChordAction();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
+                if (start) {
+                    interceptAccessibilityShortcutChord();
+                } else {
+                    cancelPendingAccessibilityShortcutAction();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
+                if (start) {
+                    interceptRingerToggleChord();
+                } else {
+                    cancelPendingRingerToggleChordAction();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
+                if (start) {
+                    performHapticFeedback(
+                            HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
+                            "KEY_GESTURE_TYPE_GLOBAL_ACTIONS - Global Actions");
+                    showGlobalActions();
+                } else {
+                    cancelGlobalActionsAction();
+                }
+                return true;
+                // TODO (b/358569822): Consolidate TV and non-TV gestures into same KeyGestureEvent
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
+                if (start) {
+                    interceptAccessibilityGestureTv();
+                } else {
+                    cancelAccessibilityGestureTv();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
+                if (start) {
+                    interceptBugreportGestureTv();
+                } else {
+                    cancelBugreportGestureTv();
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
+                if (complete && mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
+                        isKeyguardLocked())) {
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
+                }
+                return true;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
+                if (complete) {
+                    mContext.closeSystemDialogs();
+                }
+                return true;
         }
         return false;
     }
@@ -4177,7 +4390,8 @@
         mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
     }
 
-    private void requestBugreportForTv() {
+    @VisibleForTesting
+    void requestBugreportForTv() {
         try {
             if (!ActivityManager.getService().launchBugReportHandlerApp()) {
                 ActivityManager.getService().requestInteractiveBugReport();
@@ -4190,7 +4404,7 @@
     // TODO(b/117479243): handle it in InputPolicy
     /** {@inheritDoc} */
     @Override
-    public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
+    public boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
         // Note: This method is only called if the initial down was unhandled.
         if (DEBUG_INPUT) {
             final KeyInterceptionInfo info =
@@ -4203,75 +4417,26 @@
                     + ", keyCode=" + event.getKeyCode()
                     + ", scanCode=" + event.getScanCode()
                     + ", metaState=" + event.getMetaState()
-                    + ", repeatCount=" + event.getRepeatCount()
-                    + ", policyFlags=" + policyFlags);
+                    + ", repeatCount=" + event.getRepeatCount());
         }
 
-        if (interceptUnhandledKey(event, focusedToken)) {
-            return null;
-        }
-
-        KeyEvent fallbackEvent = null;
-        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
-            final KeyCharacterMap kcm = event.getKeyCharacterMap();
-            final int keyCode = event.getKeyCode();
-            final int metaState = event.getMetaState();
-            final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
-                    && event.getRepeatCount() == 0;
-
-            // Check for fallback actions specified by the key character map.
-            final FallbackAction fallbackAction;
-            if (initialDown) {
-                fallbackAction = kcm.getFallbackAction(keyCode, metaState);
-            } else {
-                fallbackAction = mFallbackActions.get(keyCode);
-            }
-
-            if (fallbackAction != null) {
-                if (DEBUG_INPUT) {
-                    Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
-                            + " metaState=" + Integer.toHexString(fallbackAction.metaState));
-                }
-
-                final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
-                fallbackEvent = KeyEvent.obtain(
-                        event.getDownTime(), event.getEventTime(),
-                        event.getAction(), fallbackAction.keyCode,
-                        event.getRepeatCount(), fallbackAction.metaState,
-                        event.getDeviceId(), event.getScanCode(),
-                        flags, event.getSource(), event.getDisplayId(), null);
-
-                if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
-                    fallbackEvent.recycle();
-                    fallbackEvent = null;
-                }
-
-                if (initialDown) {
-                    mFallbackActions.put(keyCode, fallbackAction);
-                } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                    mFallbackActions.remove(keyCode);
-                    fallbackAction.recycle();
-                }
-            }
-        }
-
-        if (DEBUG_INPUT) {
-            if (fallbackEvent == null) {
-                Slog.d(TAG, "No fallback.");
-            } else {
-                Slog.d(TAG, "Performing fallback: " + fallbackEvent);
-            }
-        }
-        return fallbackEvent;
-    }
-
-    private boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
         final int keyCode = event.getKeyCode();
         final int repeatCount = event.getRepeatCount();
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final int metaState = event.getModifiers();
 
-        switch(keyCode) {
+        // TODO(b/358569822): Shift to KeyGestureEvent based handling
+        if (keyCode == KeyEvent.KEYCODE_STEM_PRIMARY) {
+            handleUnhandledSystemKey(event);
+            sendSystemKeyToStatusBarAsync(event);
+            return true;
+        }
+
+        if (useKeyGestureEventHandler()) {
+            return false;
+        }
+
+        switch (keyCode) {
             case KeyEvent.KEYCODE_SPACE:
                 if (down && repeatCount == 0) {
                     // Handle keyboard layout switching. (CTRL + SPACE)
@@ -4308,10 +4473,6 @@
                     return true;
                 }
                 break;
-            case KeyEvent.KEYCODE_STEM_PRIMARY:
-                handleUnhandledSystemKey(event);
-                sendSystemKeyToStatusBarAsync(event);
-                return true;
         }
 
         return false;
@@ -4350,19 +4511,6 @@
                 targetWindowToken);
     }
 
-    private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
-            int policyFlags) {
-        int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
-        if ((actions & ACTION_PASS_TO_USER) != 0) {
-            long delayMillis = interceptKeyBeforeDispatching(
-                    focusedToken, fallbackEvent, policyFlags);
-            if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     @Override
     public void setTopFocusedDisplay(int displayId) {
         mTopFocusedDisplayId = displayId;
@@ -4819,6 +4967,15 @@
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                 || event.isWakeKey();
+        boolean isKeyGestureTriggered = (policyFlags & FLAG_KEY_GESTURE_TRIGGERED) != 0;
+
+        // There are key events that perform the operation as the current user,
+        // and these should be ignored for visible background users.
+        if (mVisibleBackgroundUsersEnabled
+                && KEY_CODES_IGNORED_FOR_VISIBLE_BACKGROUND_USERS.contains(keyCode)
+                && !isKeyEventForCurrentUser(event.getDisplayId(), keyCode, null)) {
+            return 0;
+        }
 
         if (!mSystemBooted) {
             // If we have not yet booted, don't let key events do anything.
@@ -4937,8 +5094,13 @@
         final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
         final boolean isDefaultDisplayAwake = mDefaultDisplayPolicy.isAwake();
         final boolean interactiveAndAwake = interactive && isDefaultDisplayAwake;
-        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
-            handleKeyGesture(event, interactiveAndAwake, isDefaultDisplayOn);
+        if (isKeyGestureTriggered) {
+            // If key gesture is triggered outside policy, reset gesture handlers here
+            mSingleKeyGestureDetector.reset();
+        } else {
+            if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+                handleKeyGesture(event, interactiveAndAwake, isDefaultDisplayOn);
+            }
         }
 
         // Enable haptics if down and virtual key without multiple repetitions. If this is a hard
@@ -5101,7 +5263,7 @@
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
                 if (down) {
-                    interceptPowerKeyDown(event, interactiveAndAwake);
+                    interceptPowerKeyDown(event, interactiveAndAwake, isKeyGestureTriggered);
                 } else {
                     interceptPowerKeyUp(event, canceled);
                 }
@@ -5827,6 +5989,10 @@
     }
 
     private void wakeUpFromWakeKey(KeyEvent event) {
+        if (!isKeyEventForCurrentUser(
+                event.getDisplayId(), event.getKeyCode(), "wakeUpFromWakeKey")) {
+            return;
+        }
         wakeUpFromWakeKey(
                 event.getEventTime(),
                 event.getKeyCode(),
@@ -6406,6 +6572,12 @@
     // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display.
     @Override
     public void setAllowLockscreenWhenOn(int displayId, boolean allow) {
+        // We should ignore this operation for visible background users
+        // until lockscreen supports multi-display.
+        if (mVisibleBackgroundUsersEnabled
+                && mUserManagerInternal.getUserAssignedToDisplay(displayId) != mCurrentUserId) {
+            return;
+        }
         if (allow) {
             mAllowLockscreenWhenOnDisplays.add(displayId);
         } else {
@@ -7214,4 +7386,29 @@
         }
         return DEFAULT_DISPLAY;
     }
+
+    /**
+     * This method is intended to prevent key events for visible background users
+     * from interfering with the current user's experience in MUMD environment.
+     *
+     * @param displayId the displayId of the key event.
+     * @param keyCode the key code of the event.
+     *
+     * @return false if the key event is for a visible background user.
+     */
+    private boolean isKeyEventForCurrentUser(int displayId, int keyCode, @Nullable String purpose) {
+        if (!mVisibleBackgroundUsersEnabled) {
+            return true;
+        }
+        int assignedUser = mUserManagerInternal.getUserAssignedToDisplay(displayId);
+        if (assignedUser == mCurrentUserId) {
+            return true;
+        }
+        if (DEBUG_INPUT) {
+            Slog.w(TAG, "Cannot handle " + KeyEvent.keyCodeToString(keyCode)
+                    + (purpose != null ? " to " + purpose : "")
+                    + " for visible background user(u" + assignedUser + ")");
+        }
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index a060f50..441d3ea 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -105,9 +105,9 @@
 
         /**
          *  Maximum count of multi presses.
-         *  Return 1 will trigger onPress immediately when {@link KeyEvent.ACTION_UP}.
+         *  Return 1 will trigger onPress immediately when {@link KeyEvent#ACTION_UP}.
          *  Otherwise trigger onMultiPress immediately when reach max count when
-         *  {@link KeyEvent.ACTION_DOWN}.
+         *  {@link KeyEvent#ACTION_DOWN}.
          */
         int getMaxMultiPressCount() {
             return 1;
@@ -153,8 +153,10 @@
          * @param eventTime  the timestamp of this event
          * @param pressCount the number of presses detected leading up to this key up event
          * @param displayId  the display ID of the event
+         * @param deviceId   the ID of the input device that generated this event
+         * @param metaState  the state of the modifiers when this gesture was detected
          */
-        void onKeyUp(long eventTime, int pressCount, int displayId) {}
+        void onKeyUp(long eventTime, int pressCount, int displayId, int deviceId, int metaState) {}
 
         @Override
         public String toString() {
@@ -183,7 +185,11 @@
     }
 
     private record MessageObject(SingleKeyRule activeRule, int keyCode, int pressCount,
-                                 int displayId) {
+                                 int displayId, int metaState, int deviceId) {
+        MessageObject(SingleKeyRule activeRule, int keyCode, int pressCount, KeyEvent event) {
+            this(activeRule, keyCode, pressCount, event.getDisplayId(), event.getMetaState(),
+                    event.getDeviceId());
+        }
     }
 
     static SingleKeyGestureDetector get(Context context, Looper looper) {
@@ -236,7 +242,7 @@
                 mHandler.removeMessages(MSG_KEY_LONG_PRESS);
                 mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
                 MessageObject object = new MessageObject(mActiveRule, keyCode, /* pressCount= */ 1,
-                        event.getDisplayId());
+                        event);
                 final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, object);
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
@@ -284,7 +290,7 @@
         if (mKeyPressCounter == 1) {
             if (mActiveRule.supportLongPress()) {
                 MessageObject object = new MessageObject(mActiveRule, keyCode, mKeyPressCounter,
-                        event.getDisplayId());
+                        event);
                 final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, object);
                 msg.setAsynchronous(true);
                 mHandler.sendMessageDelayed(msg, mActiveRule.getLongPressTimeoutMs());
@@ -292,7 +298,7 @@
 
             if (mActiveRule.supportVeryLongPress()) {
                 MessageObject object = new MessageObject(mActiveRule, keyCode, mKeyPressCounter,
-                        event.getDisplayId());
+                        event);
                 final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, object);
                 msg.setAsynchronous(true);
                 mHandler.sendMessageDelayed(msg, mActiveRule.getVeryLongPressTimeoutMs());
@@ -310,7 +316,7 @@
                             + " reached the max count " + mKeyPressCounter);
                 }
                 MessageObject object = new MessageObject(mActiveRule, keyCode, mKeyPressCounter,
-                        event.getDisplayId());
+                        event);
                 final Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, object);
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
@@ -351,7 +357,7 @@
         if (event.getKeyCode() == mActiveRule.mKeyCode) {
             // key-up action should always be triggered if not processed by long press.
             MessageObject object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
-                    mKeyPressCounter, event.getDisplayId());
+                    mKeyPressCounter, event);
             Message msgKeyUp = mHandler.obtainMessage(MSG_KEY_UP, object);
             msgKeyUp.setAsynchronous(true);
             mHandler.sendMessage(msgKeyUp);
@@ -362,7 +368,7 @@
                     Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
                 }
                 object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
-                        /* pressCount= */ 1, event.getDisplayId());
+                        /* pressCount= */ 1, event);
                 Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, object);
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
@@ -373,7 +379,7 @@
             // This could be a multi-press.  Wait a little bit longer to confirm.
             if (mKeyPressCounter < mActiveRule.getMaxMultiPressCount()) {
                 object = new MessageObject(mActiveRule, mActiveRule.mKeyCode,
-                        mKeyPressCounter, event.getDisplayId());
+                        mKeyPressCounter, event);
                 Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, object);
                 msg.setAsynchronous(true);
                 mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
@@ -452,7 +458,8 @@
                         Log.i(TAG, "Detect key up " + KeyEvent.keyCodeToString(keyCode)
                                 + " on display " + displayId);
                     }
-                    rule.onKeyUp(mLastDownTime, pressCount, displayId);
+                    rule.onKeyUp(mLastDownTime, pressCount, displayId, object.deviceId,
+                            object.metaState);
                     break;
                 case MSG_KEY_LONG_PRESS:
                     if (DEBUG) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 892af6b..ad11657 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -754,11 +754,9 @@
      * @param focusedToken Client window token that currently has focus. This is where the key
      *            event will normally go.
      * @param event The key event.
-     * @param policyFlags The policy flags associated with the key.
-     * @return Returns an alternate key event to redispatch as a fallback, or null to give up.
-     * The caller is responsible for recycling the key event.
+     * @return true if the unhandled key is intercepted by the policy.
      */
-    KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags);
+    boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken);
 
     /**
      * Called when the top focused display is changed.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 303828f..0cdf537 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -53,6 +53,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -512,8 +513,17 @@
             }
 
             // Start input as soon as we start waking up or going to sleep.
-            mInputManagerInternal.setInteractive(interactive);
             mInputMethodManagerInternal.setInteractive(interactive);
+            if (!mFlags.isPerDisplayWakeByTouchEnabled()) {
+                // Since wakefulness is a global property in original logic, all displays should
+                // be set to the same interactive state, matching system's global wakefulness
+                SparseBooleanArray displayInteractivities = new SparseBooleanArray();
+                int[] displayIds = mDisplayManagerInternal.getDisplayIds().toArray();
+                for (int displayId : displayIds) {
+                    displayInteractivities.put(displayId, interactive);
+                }
+                mInputManagerInternal.setDisplayInteractivities(displayInteractivities);
+            }
 
             // Notify battery stats.
             try {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 71cb882..e053964 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2207,7 +2207,7 @@
                     + ", groupId=" + powerGroup.getGroupId()
                     + ", reason=" + PowerManager.wakeReasonToString(reason) + ", uid=" + uid);
         }
-        if (mForceSuspendActive || !mSystemReady) {
+        if (mForceSuspendActive || !mSystemReady || (powerGroup == null)) {
             return;
         }
         powerGroup.wakeUpLocked(eventTime, reason, details, uid, opPackageName, opUid,
@@ -6027,6 +6027,12 @@
         @Override // Binder call
         public void wakeUp(long eventTime, @WakeReason int reason, String details,
                 String opPackageName) {
+            wakeUpWithDisplayId(eventTime, reason, details, opPackageName, Display.DEFAULT_DISPLAY);
+        }
+
+        @Override // Binder call
+        public void wakeUpWithDisplayId(long eventTime, @WakeReason int reason, String details,
+                String opPackageName, int displayId) {
             final long now = mClock.uptimeMillis();
             if (eventTime > now) {
                 Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now);
@@ -6039,13 +6045,14 @@
             final int uid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
+                int displayGroupId = getDisplayGroupId(displayId);
                 synchronized (mLock) {
                     if (!mBootCompleted && sQuiescent) {
                         mDirty |= DIRTY_QUIESCENT;
                         updatePowerStateLocked();
                         return;
                     }
-                    wakePowerGroupLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), eventTime,
+                    wakePowerGroupLocked(mPowerGroups.get(displayGroupId), eventTime,
                             reason, details, uid, opPackageName, uid);
                 }
             } finally {
@@ -7335,4 +7342,12 @@
             }
         }
     }
+
+    private int getDisplayGroupId(int displayId) {
+        DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
+        if (displayInfo == null) {
+            return Display.INVALID_DISPLAY_GROUP;
+        }
+        return displayInfo.displayGroupId;
+    }
 }
diff --git a/services/core/java/com/android/server/power/PowerManagerShellCommand.java b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
index 20184e9f..f69a017 100644
--- a/services/core/java/com/android/server/power/PowerManagerShellCommand.java
+++ b/services/core/java/com/android/server/power/PowerManagerShellCommand.java
@@ -16,13 +16,19 @@
 
 package com.android.server.power;
 
+import android.app.AlarmManager;
+import android.app.IAlarmCompleteListener;
+import android.app.IAlarmListener;
+import android.app.IAlarmManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.PowerManagerInternal;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.ShellCommand;
+import android.os.SystemClock;
 import android.util.SparseArray;
 import android.view.Display;
 
@@ -34,12 +40,26 @@
 
     private final Context mContext;
     private final PowerManagerService.BinderService mService;
+    private final IAlarmListener mAlarmListener;
+    private IAlarmManager mAlarmManager;
 
     private SparseArray<WakeLock> mProxWakelocks = new SparseArray<>();
 
     PowerManagerShellCommand(Context context, PowerManagerService.BinderService service) {
         mContext = context;
         mService = service;
+        mAlarmManager =
+            IAlarmManager.Stub.asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+        mAlarmListener = new IAlarmListener.Stub() {
+            @Override
+            public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+                mService.wakeUp(
+                        SystemClock.uptimeMillis(),
+                        PowerManager.WAKE_REASON_APPLICATION,
+                        "PowerManagerShellCommand",
+                        mContext.getOpPackageName());
+            }
+        };
     }
 
     @Override
@@ -65,6 +85,10 @@
                     return runSetProx();
                 case "set-face-down-detector":
                     return runSetFaceDownDetector();
+                case "sleep":
+                    return runSleep();
+                case "wakeup":
+                    return runWakeUp();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -194,6 +218,70 @@
         return 0;
     }
 
+    private int runSleep() {
+        try {
+            mService.goToSleep(
+                    SystemClock.uptimeMillis(),
+                    PowerManager.GO_TO_SLEEP_REASON_APPLICATION,
+                    PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+        } catch (Exception e) {
+            final PrintWriter pw = getOutPrintWriter();
+            pw.println("Error: " + e);
+            return -1;
+        }
+        return 0;
+    }
+
+    private int runWakeUp() {
+        final PrintWriter pw = getOutPrintWriter();
+        String delay = getNextArg();
+        if (delay == null) {
+            try {
+                mService.wakeUp(
+                        SystemClock.uptimeMillis(),
+                        PowerManager.WAKE_REASON_APPLICATION,
+                        "PowerManagerShellCommand",
+                        mContext.getOpPackageName());
+            } catch (Exception e) {
+                pw.println("Error: " + e);
+                return -1;
+            }
+        } else {
+            long delayMillis;
+            try {
+                delayMillis = Long.parseLong(delay);
+            } catch (NumberFormatException e) {
+                pw.println("Error: Can't parse arg " + delay + " as a long: " + e);
+                return -1;
+            }
+            if (delayMillis < 0) {
+                pw.println("Error: Can't set a negative delay: " + delayMillis);
+                return -1;
+            }
+            long wakeUpTime = System.currentTimeMillis() + delayMillis;
+            if (mAlarmManager == null) {
+                // PowerManagerShellCommand may be initialized before AlarmManagerService
+                // is brought up. Make sure mAlarmManager exists.
+                mAlarmManager = IAlarmManager.Stub.asInterface(
+                        ServiceManager.getService(Context.ALARM_SERVICE));
+            }
+            try {
+                // This command is called by the shell, which has "com.android.shell" as package
+                // name.
+                pw.println("Schedule an alarm to wakeup in "
+                        + delayMillis + " ms, on behalf of shell.");
+                mAlarmManager.set("com.android.shell",
+                        AlarmManager.RTC_WAKEUP, wakeUpTime,
+                        0, 0, AlarmManager.FLAG_PRIORITIZE,
+                        null, mAlarmListener, "PowerManagerShellCommand", null, null);
+            } catch (Exception e) {
+                pw.println("Error: " + e);
+                return -1;
+            }
+        }
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -221,6 +309,11 @@
         pw.println("    created by set-prox including their held status.");
         pw.println("  set-face-down-detector [true|false]");
         pw.println("    sets whether we use face down detector timeouts or not");
+        pw.println("  sleep");
+        pw.println("    requests to sleep the device");
+        pw.println("  wakeup <delay>");
+        pw.println("    requests to wake up the device. If a delay of milliseconds is specified,");
+        pw.println("    alarm manager will schedule a wake up after the delay.");
 
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index c6ef89d..fd60e06 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -42,6 +42,11 @@
             Flags::improveWakelockLatency
     );
 
+    private final FlagState mPerDisplayWakeByTouch = new FlagState(
+            Flags.FLAG_PER_DISPLAY_WAKE_BY_TOUCH,
+            Flags::perDisplayWakeByTouch
+    );
+
     /** Returns whether early-screen-timeout-detector is enabled on not. */
     public boolean isEarlyScreenTimeoutDetectorEnabled() {
         return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -55,6 +60,13 @@
     }
 
     /**
+     * @return Whether per-display wake by touch is enabled or not.
+     */
+    public boolean isPerDisplayWakeByTouchEnabled() {
+        return mPerDisplayWakeByTouch.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
@@ -62,6 +74,7 @@
         pw.println("PowerManagerFlags:");
         pw.println(" " + mEarlyScreenTimeoutDetectorFlagState);
         pw.println(" " + mImproveWakelockLatency);
+        pw.println(" " + mPerDisplayWakeByTouch);
     }
 
     private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index 3581b2f..9cf3bb6 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -17,4 +17,12 @@
     description: "Feature flag for tracking the optimizations to improve the latency of acquiring and releasing a wakelock."
     bug: "339590565"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    name: "per_display_wake_by_touch"
+    namespace: "power"
+    description: "Feature flag to enable per-display wake by touch"
+    bug: "343295183"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/power/stats/AccumulatedBatteryUsageStatsSection.java b/services/core/java/com/android/server/power/stats/AccumulatedBatteryUsageStatsSection.java
new file mode 100644
index 0000000..dd6d5db
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AccumulatedBatteryUsageStatsSection.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.BatteryUsageStats;
+import android.util.IndentingPrintWriter;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class AccumulatedBatteryUsageStatsSection extends PowerStatsSpan.Section {
+    public static final String TYPE = "accumulated-battery-usage-stats";
+    public static final long ID = Long.MAX_VALUE;
+
+    private final BatteryUsageStats.Builder mBatteryUsageStats;
+
+    AccumulatedBatteryUsageStatsSection(BatteryUsageStats.Builder batteryUsageStats) {
+        super(TYPE);
+        mBatteryUsageStats = batteryUsageStats;
+    }
+
+    public BatteryUsageStats.Builder getBatteryUsageStatsBuilder() {
+        return mBatteryUsageStats;
+    }
+
+    @Override
+    public void write(TypedXmlSerializer serializer) throws IOException {
+        mBatteryUsageStats.build().writeXml(serializer);
+    }
+
+    @Override
+    public void dump(IndentingPrintWriter ipw) {
+        mBatteryUsageStats.build().dump(ipw, "");
+    }
+
+    static class Reader implements PowerStatsSpan.SectionReader {
+        @Override
+        public String getType() {
+            return TYPE;
+        }
+
+        @Override
+        public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            return new AccumulatedBatteryUsageStatsSection(
+                    BatteryUsageStats.createBuilderFromXml(parser));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index cb8e1a0..3f1d9a3 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -509,6 +509,7 @@
     }
 
     private boolean mSaveBatteryUsageStatsOnReset;
+    private boolean mAccumulateBatteryUsageStats;
     private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
     private PowerStatsStore mPowerStatsStore;
 
@@ -11975,10 +11976,12 @@
      */
     public void saveBatteryUsageStatsOnReset(
             @NonNull BatteryUsageStatsProvider batteryUsageStatsProvider,
-            @NonNull PowerStatsStore powerStatsStore) {
+            @NonNull PowerStatsStore powerStatsStore,
+            boolean accumulateBatteryUsageStats) {
         mSaveBatteryUsageStatsOnReset = true;
         mBatteryUsageStatsProvider = batteryUsageStatsProvider;
         mPowerStatsStore = powerStatsStore;
+        mAccumulateBatteryUsageStats = accumulateBatteryUsageStats;
     }
 
     @GuardedBy("this")
@@ -12179,29 +12182,33 @@
             return;
         }
 
-        final BatteryUsageStats batteryUsageStats;
-        synchronized (this) {
-            batteryUsageStats = mBatteryUsageStatsProvider.getBatteryUsageStats(this,
-                    new BatteryUsageStatsQuery.Builder()
-                            .setMaxStatsAgeMs(0)
-                            .includePowerModels()
-                            .includeProcessStateData()
-                            .build());
-        }
-
-        // TODO(b/188068523): BatteryUsageStats should use monotonic time for start and end
-        // Once that change is made, we will be able to use the BatteryUsageStats' monotonic
-        // start time
-        long monotonicStartTime =
-                mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
-        mHandler.post(() -> {
-            mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats);
-            try {
-                batteryUsageStats.close();
-            } catch (IOException e) {
-                Log.e(TAG, "Cannot close BatteryUsageStats", e);
+        if (mAccumulateBatteryUsageStats) {
+            mBatteryUsageStatsProvider.accumulateBatteryUsageStats(this);
+        } else {
+            final BatteryUsageStats batteryUsageStats;
+            synchronized (this) {
+                batteryUsageStats = mBatteryUsageStatsProvider.getBatteryUsageStats(this,
+                        new BatteryUsageStatsQuery.Builder()
+                                .setMaxStatsAgeMs(0)
+                                .includePowerModels()
+                                .includeProcessStateData()
+                                .build());
             }
-        });
+
+            // TODO(b/188068523): BatteryUsageStats should use monotonic time for start and end
+            // Once that change is made, we will be able to use the BatteryUsageStats' monotonic
+            // start time
+            long monotonicStartTime =
+                    mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
+            mHandler.post(() -> {
+                mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats);
+                try {
+                    batteryUsageStats.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Cannot close BatteryUsageStats", e);
+                }
+            });
+        }
     }
 
     @GuardedBy("this")
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 87a3e5e..d66e05b 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -33,6 +33,7 @@
 import com.android.internal.os.CpuScalingPolicies;
 import com.android.internal.os.PowerProfile;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -64,6 +65,7 @@
         mClock = clock;
 
         mPowerStatsStore.addSectionReader(new BatteryUsageStatsSection.Reader());
+        mPowerStatsStore.addSectionReader(new AccumulatedBatteryUsageStatsSection.Reader());
     }
 
     private List<PowerCalculator> getPowerCalculators() {
@@ -151,6 +153,56 @@
     }
 
     /**
+     * Compute BatteryUsageStats for the period since the last accumulated stats were stored,
+     * add them to the accumulated stats and save the result.
+     */
+    public void accumulateBatteryUsageStats(BatteryStatsImpl stats) {
+        BatteryUsageStats.Builder accumulatedBatteryUsageStatsBuilder = null;
+
+        PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan(
+                AccumulatedBatteryUsageStatsSection.ID,
+                AccumulatedBatteryUsageStatsSection.TYPE);
+        if (powerStatsSpan != null) {
+            List<PowerStatsSpan.Section> sections = powerStatsSpan.getSections();
+            for (int i = sections.size() - 1; i >= 0; i--) {
+                PowerStatsSpan.Section section = sections.get(i);
+                if (AccumulatedBatteryUsageStatsSection.TYPE.equals(section.getType())) {
+                    accumulatedBatteryUsageStatsBuilder =
+                            ((AccumulatedBatteryUsageStatsSection) section)
+                                    .getBatteryUsageStatsBuilder();
+                    break;
+                }
+            }
+        }
+
+        // TODO(b/366493365): add the current batteryusagestats directly into the "accumulated"
+        // builder to avoid allocating a second CursorWindow
+        BatteryUsageStats.Builder currentBatteryUsageStatsBuilder =
+                getCurrentBatteryUsageStatsBuilder(stats,
+                        new BatteryUsageStatsQuery.Builder()
+                                .setMaxStatsAgeMs(0)
+                                .includeProcessStateData()
+                                .includePowerStateData()
+                                .includeScreenStateData()
+                                .build(),
+                        mClock.currentTimeMillis());
+
+        if (accumulatedBatteryUsageStatsBuilder == null) {
+            accumulatedBatteryUsageStatsBuilder = currentBatteryUsageStatsBuilder;
+        } else {
+            accumulatedBatteryUsageStatsBuilder.add(currentBatteryUsageStatsBuilder.build());
+            currentBatteryUsageStatsBuilder.discard();
+        }
+
+        powerStatsSpan = new PowerStatsSpan(AccumulatedBatteryUsageStatsSection.ID);
+        powerStatsSpan.addSection(
+                new AccumulatedBatteryUsageStatsSection(accumulatedBatteryUsageStatsBuilder));
+
+        mPowerStatsStore.storePowerStatsSpanAsync(powerStatsSpan,
+                accumulatedBatteryUsageStatsBuilder::discard);
+    }
+
+    /**
      * Returns true if the last update was too long ago for the tolerances specified
      * by the supplied queries.
      */
@@ -192,15 +244,67 @@
 
     private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats,
             BatteryUsageStatsQuery query, long currentTimeMs) {
-        if (query.getToTimestamp() == 0) {
+        if ((query.getFlags()
+                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_ACCUMULATED) != 0) {
+            return getAccumulatedBatteryUsageStats(stats, query);
+        } else if (query.getToTimestamp() == 0) {
             return getCurrentBatteryUsageStats(stats, query, currentTimeMs);
         } else {
             return getAggregatedBatteryUsageStats(stats, query);
         }
     }
 
+    private BatteryUsageStats getAccumulatedBatteryUsageStats(BatteryStatsImpl stats,
+            BatteryUsageStatsQuery query) {
+        PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan(
+                AccumulatedBatteryUsageStatsSection.ID,
+                AccumulatedBatteryUsageStatsSection.TYPE);
+
+        BatteryUsageStats.Builder accumulatedBatteryUsageStatsBuilder = null;
+        if (powerStatsSpan != null) {
+            List<PowerStatsSpan.Section> sections = powerStatsSpan.getSections();
+            if (sections.size() == 1) {
+                accumulatedBatteryUsageStatsBuilder =
+                        ((AccumulatedBatteryUsageStatsSection) sections.get(0))
+                                .getBatteryUsageStatsBuilder();
+            } else {
+                Slog.wtf(TAG, "Unexpected number of sections for type "
+                        + AccumulatedBatteryUsageStatsSection.TYPE);
+            }
+        }
+
+        BatteryUsageStats currentBatteryUsageStats = getCurrentBatteryUsageStats(stats, query,
+                mClock.currentTimeMillis());
+
+        BatteryUsageStats result;
+        if (accumulatedBatteryUsageStatsBuilder == null) {
+            result = currentBatteryUsageStats;
+        } else {
+            accumulatedBatteryUsageStatsBuilder.add(currentBatteryUsageStats);
+            try {
+                currentBatteryUsageStats.close();
+            } catch (IOException ex) {
+                Slog.e(TAG, "Closing BatteryUsageStats", ex);
+            }
+            result = accumulatedBatteryUsageStatsBuilder.build();
+        }
+
+        return result;
+    }
+
     private BatteryUsageStats getCurrentBatteryUsageStats(BatteryStatsImpl stats,
             BatteryUsageStatsQuery query, long currentTimeMs) {
+        BatteryUsageStats.Builder builder = getCurrentBatteryUsageStatsBuilder(stats, query,
+                currentTimeMs);
+        BatteryUsageStats batteryUsageStats = builder.build();
+        if (batteryUsageStats.isProcessStateDataIncluded()) {
+            verify(batteryUsageStats);
+        }
+        return batteryUsageStats;
+    }
+
+    private BatteryUsageStats.Builder getCurrentBatteryUsageStatsBuilder(BatteryStatsImpl stats,
+            BatteryUsageStatsQuery query, long currentTimeMs) {
         final long realtimeUs = mClock.elapsedRealtime() * 1000;
         final long uptimeUs = mClock.uptimeMillis() * 1000;
 
@@ -274,11 +378,7 @@
         mPowerAttributor.estimatePowerConsumption(batteryUsageStatsBuilder, stats.getHistory(),
                 monotonicStartTime, monotonicEndTime);
 
-        BatteryUsageStats batteryUsageStats = batteryUsageStatsBuilder.build();
-        if (includeProcessStateData) {
-            verify(batteryUsageStats);
-        }
-        return batteryUsageStats;
+        return batteryUsageStatsBuilder;
     }
 
     // STOPSHIP(b/229906525): remove verification before shipping
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsStore.java b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
index a875c30..5a6f973 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsStore.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
@@ -133,6 +133,19 @@
     }
 
     /**
+     * Schedules saving the specified span on the background thread.
+     */
+    public void storePowerStatsSpanAsync(PowerStatsSpan span, Runnable onComplete) {
+        mHandler.post(() -> {
+            try {
+                storePowerStatsSpan(span);
+            } finally {
+                onComplete.run();
+            }
+        });
+    }
+
+    /**
      * Saves the specified span in the store.
      */
     public void storePowerStatsSpan(PowerStatsSpan span) {
@@ -172,6 +185,9 @@
         lockStoreDirectory();
         try {
             File file = makePowerStatsSpanFilename(id);
+            if (!file.exists()) {
+                return null;
+            }
             try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
                 return PowerStatsSpan.read(inputStream, parser, mSectionReaders, sectionTypes);
             } catch (IOException | XmlPullParserException e) {
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index 05d29f5..ce6f57f 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -76,3 +76,13 @@
     bug: "364350206"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "accumulate_battery_usage_stats"
+    namespace: "backstage_power"
+    description: "Add support for monotonically accumulated BatteryUsageStats"
+    bug: "345022340"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
index f047f56..ab630ee 100644
--- a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
@@ -17,6 +17,7 @@
 package com.android.server.power.stats.wakeups;
 
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_BLUETOOTH;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
@@ -63,6 +64,7 @@
     private static final String SUBSYSTEM_SOUND_TRIGGER_STRING = "Sound_trigger";
     private static final String SUBSYSTEM_SENSOR_STRING = "Sensor";
     private static final String SUBSYSTEM_CELLULAR_DATA_STRING = "Cellular_data";
+    private static final String SUBSYSTEM_BLUETOOTH_STRING = "Bluetooth";
     private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
 
     private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
@@ -512,6 +514,8 @@
                 return CPU_WAKEUP_SUBSYSTEM_SENSOR;
             case SUBSYSTEM_CELLULAR_DATA_STRING:
                 return CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
+            case SUBSYSTEM_BLUETOOTH_STRING:
+                return CPU_WAKEUP_SUBSYSTEM_BLUETOOTH;
         }
         return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
     }
@@ -528,6 +532,8 @@
                 return SUBSYSTEM_SENSOR_STRING;
             case CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA:
                 return SUBSYSTEM_CELLULAR_DATA_STRING;
+            case CPU_WAKEUP_SUBSYSTEM_BLUETOOTH:
+                return SUBSYSTEM_BLUETOOTH_STRING;
             case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
                 return "Unknown";
         }
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index e3d71e4..f78c448 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -81,7 +81,7 @@
 public final class RollbackPackageHealthObserver implements PackageHealthObserver {
     private static final String TAG = "RollbackPackageHealthObserver";
     private static final String NAME = "rollback-observer";
-    private static final String ACTION_NAME = RollbackPackageHealthObserver.class.getName();
+    private static final String CLASS_NAME = RollbackPackageHealthObserver.class.getName();
 
     private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
             | ApplicationInfo.FLAG_SYSTEM;
@@ -610,14 +610,16 @@
                 }
             };
 
+            String intentActionName = CLASS_NAME + rollback.getRollbackId();
             // Register the BroadcastReceiver
             mContext.registerReceiver(rollbackReceiver,
-                    new IntentFilter(ACTION_NAME),
+                    new IntentFilter(intentActionName),
                     Context.RECEIVER_NOT_EXPORTED);
 
-            Intent intentReceiver = new Intent(ACTION_NAME);
+            Intent intentReceiver = new Intent(intentActionName);
             intentReceiver.putExtra("rollbackId", rollback.getRollbackId());
             intentReceiver.setPackage(mContext.getPackageName());
+            intentReceiver.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
 
             PendingIntent rollbackPendingIntent = PendingIntent.getBroadcast(mContext,
                     rollback.getRollbackId(),
diff --git a/services/core/java/com/android/server/security/forensic/OWNERS b/services/core/java/com/android/server/security/forensic/OWNERS
new file mode 100644
index 0000000..3bc3eb5
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:main:/core/java/android/security/forensic/OWNERS
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b35a0a7..74c1124e 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -216,13 +216,13 @@
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
-import com.android.server.PinnerService;
-import com.android.server.PinnerService.PinnedFileStats;
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
 import com.android.server.health.HealthServiceWrapper;
 import com.android.server.notification.NotificationManagerService;
+import com.android.server.pinner.PinnerService;
+import com.android.server.pinner.PinnerService.PinnedFileStats;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.power.stats.KernelWakelockReader;
 import com.android.server.power.stats.KernelWakelockStats;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 374dd89..6405353 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -37,12 +37,14 @@
 import android.app.timedetector.TelephonyTimeSuggestion;
 import android.content.Context;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ApplicationSharedMemory;
 import com.android.server.SystemClockTime;
 import com.android.server.SystemClockTime.TimeConfidence;
 import com.android.server.timezonedetector.ArrayMapWithHistory;
@@ -315,6 +317,17 @@
         // detected time if, for example, the age of all suggestions are considered.
         NetworkTimeSuggestion lastNetworkSuggestion = mLastNetworkSuggestion.get();
         if (lastNetworkSuggestion == null || !lastNetworkSuggestion.equals(suggestion)) {
+            if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
+                    && android.os.Flags.networkTimeUsesSharedMemory()) {
+                UnixEpochTime networkUnixEpochTime = suggestion.getUnixEpochTime();
+                long lastNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis =
+                        networkUnixEpochTime.getUnixEpochTimeMillis()
+                                - networkUnixEpochTime.getElapsedRealtimeMillis();
+                ApplicationSharedMemory.getInstance()
+                        .setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(
+                                lastNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis);
+            }
+
             mLastNetworkSuggestion.set(suggestion);
             notifyNetworkTimeUpdateListenersAsynchronously();
         }
@@ -347,8 +360,12 @@
 
     @Override
     public synchronized void clearLatestNetworkSuggestion() {
+        if (com.android.internal.os.Flags.applicationSharedMemoryEnabled()
+                && android.os.Flags.networkTimeUsesSharedMemory()) {
+            ApplicationSharedMemory.getInstance()
+                    .clearLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
+        }
         mLastNetworkSuggestion.set(null);
-
         notifyNetworkTimeUpdateListenersAsynchronously();
 
         // The loss of network time may change the time signal to use to set the system clock.
diff --git a/services/core/java/com/android/server/utils/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java
index 153bb91..1ba2487 100644
--- a/services/core/java/com/android/server/utils/AnrTimer.java
+++ b/services/core/java/com/android/server/utils/AnrTimer.java
@@ -130,22 +130,35 @@
     }
 
     /**
-     * Return true if freezing is enabled.  This has no effect if the service is not enabled.
+     * Return true if freezing is feature-enabled.  Freezing must still be enabled on a
+     * per-service basis.
      */
-    private static boolean anrTimerFreezerEnabled() {
+    private static boolean freezerFeatureEnabled() {
         return Flags.anrTimerFreezer();
     }
 
     /**
+     * Return true if tracing is feature-enabled.  This has no effect unless tracing is configured.
+     * Note that this does not represent any per-process overrides via an Injector.
+     */
+    public static boolean traceFeatureEnabled() {
+        return anrTimerServiceEnabled() && Flags.anrTimerTrace();
+    }
+
+    /**
      * This class allows test code to provide instance-specific overrides.
      */
     static class Injector {
-        boolean anrTimerServiceEnabled() {
+        boolean serviceEnabled() {
             return AnrTimer.anrTimerServiceEnabled();
         }
 
-        boolean anrTimerFreezerEnabled() {
-            return AnrTimer.anrTimerFreezerEnabled();
+        boolean freezerEnabled() {
+            return AnrTimer.freezerFeatureEnabled();
+        }
+
+        boolean traceEnabled() {
+            return AnrTimer.traceFeatureEnabled();
         }
     }
 
@@ -349,7 +362,7 @@
         mWhat = what;
         mLabel = label;
         mArgs = args;
-        boolean enabled = args.mInjector.anrTimerServiceEnabled() && nativeTimersSupported();
+        boolean enabled = args.mInjector.serviceEnabled() && nativeTimersSupported();
         mFeature = createFeatureSwitch(enabled);
     }
 
@@ -448,7 +461,7 @@
 
     /**
      * The FeatureDisabled class bypasses almost all AnrTimer logic.  It is used when the AnrTimer
-     * service is disabled via Flags.anrTimerServiceEnabled.
+     * service is disabled via Flags.anrTimerService().
      */
     private class FeatureDisabled extends FeatureSwitch {
         /** Start a timer by sending a message to the client's handler. */
@@ -515,7 +528,7 @@
 
     /**
      * The FeatureEnabled class enables the AnrTimer logic.  It is used when the AnrTimer service
-     * is enabled via Flags.anrTimerServiceEnabled.
+     * is enabled via Flags.anrTimerService().
      */
     private class FeatureEnabled extends FeatureSwitch {
 
@@ -533,7 +546,7 @@
         FeatureEnabled() {
             mNative = nativeAnrTimerCreate(mLabel,
                     mArgs.mExtend,
-                    mArgs.mFreeze && mArgs.mInjector.anrTimerFreezerEnabled());
+                    mArgs.mFreeze && mArgs.mInjector.freezerEnabled());
             if (mNative == 0) throw new IllegalArgumentException("unable to create native timer");
             synchronized (sAnrTimerList) {
                 sAnrTimerList.put(mNative, new WeakReference(AnrTimer.this));
@@ -550,7 +563,7 @@
                 // exist.
                 if (cancel(arg)) mTotalRestarted++;
 
-                int timerId = nativeAnrTimerStart(mNative, pid, uid, timeoutMs);
+                final int timerId = nativeAnrTimerStart(mNative, pid, uid, timeoutMs);
                 if (timerId > 0) {
                     mTimerIdMap.put(arg, timerId);
                     mTimerArgMap.put(timerId, arg);
@@ -895,7 +908,7 @@
     /** Dumpsys output, allowing for overrides. */
     @VisibleForTesting
     static void dump(@NonNull PrintWriter pw, boolean verbose, @NonNull Injector injector) {
-        if (!injector.anrTimerServiceEnabled()) return;
+        if (!injector.serviceEnabled()) return;
 
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
         ipw.println("AnrTimer statistics");
@@ -926,6 +939,18 @@
     }
 
     /**
+     * Set a trace specification.  The input is a set of strings.  On success, the function pushes
+     * the trace specification to all timers, and then returns a response message.  On failure,
+     * the function throws IllegalArgumentException and tracing is disabled.
+     *
+     * An empty specification has no effect other than returning the current trace specification.
+     */
+    @Nullable
+    public static String traceTimers(@Nullable String[] spec) {
+        return nativeAnrTimerTrace(spec);
+    }
+
+    /**
      * Return true if the native timers are supported.  Native timers are supported if the method
      * nativeAnrTimerSupported() can be executed and it returns true.
      */
@@ -981,6 +1006,15 @@
      */
     private static native boolean nativeAnrTimerRelease(long service, int timerId);
 
+    /**
+     * Configure tracing.  The input array is a set of words pulled from the command line.  All
+     * parsing happens inside the native layer.  The function returns a string which is either an
+     * error message (so nothing happened) or the current configuration after applying the config.
+     * Passing an null array or an empty array simply returns the current configuration.
+     * The function returns null if the native layer is not implemented.
+     */
+    private static native @Nullable String nativeAnrTimerTrace(@Nullable String[] config);
+
     /** Retrieve runtime dump information from the native layer. */
     private static native String[] nativeAnrTimerDump(long service);
 }
diff --git a/services/core/java/com/android/server/utils/flags.aconfig b/services/core/java/com/android/server/utils/flags.aconfig
index 00ebb66..333287f 100644
--- a/services/core/java/com/android/server/utils/flags.aconfig
+++ b/services/core/java/com/android/server/utils/flags.aconfig
@@ -17,3 +17,10 @@
      bug: "325594551"
 }
 
+flag {
+     name: "anr_timer_trace"
+     namespace: "system_performance"
+     is_fixed_read_only: true
+     description: "When true, start a trace if an ANR timer reaches 50%"
+     bug: "352085328"
+}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 5a5f7ef..5bc2c2d 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -86,6 +86,9 @@
     private static final int MSG_EVENT_BASE = 0;
     private static final int MSG_CMD_BASE = 100;
 
+    // Copied from Settings.Global.MOBILE_DATA
+    private static final String SETTINGS_GLOBAL_MOBILE_DATA_STRING = "mobile_data";
+
     /**
      * A carrier app updated the configuration.
      *
@@ -219,7 +222,8 @@
         mContentResolver = mDeps.newVcnContentResolver(mVcnContext);
         mMobileDataSettingsObserver = new VcnMobileDataContentObserver(this /* handler */);
 
-        final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
+        // TODO: b/364740845: Replace it with DataEnabledListener
+        final Uri uri = Settings.Global.getUriFor(SETTINGS_GLOBAL_MOBILE_DATA_STRING);
         mContentResolver.registerContentObserver(
                 uri, true /* notifyForDescendants */, mMobileDataSettingsObserver);
 
diff --git a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
index b4d3862..b5a7fcb 100644
--- a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.ExternalVibration;
@@ -24,6 +25,7 @@
 import android.os.VibrationAttributes;
 import android.os.vibrator.Flags;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FrameworkStatsLog;
 
 /**
@@ -32,14 +34,16 @@
 final class ExternalVibrationSession extends Vibration
         implements VibrationSession, IBinder.DeathRecipient {
 
+    private final Object mLock = new Object();
     private final ExternalVibration mExternalVibration;
     private final ExternalVibrationScale mScale = new ExternalVibrationScale();
 
+    @GuardedBy("mLock")
     @Nullable
     private Runnable mBinderDeathCallback;
 
     ExternalVibrationSession(ExternalVibration externalVibration) {
-        super(externalVibration.getToken(), new CallerInfo(
+        super(new CallerInfo(
                 externalVibration.getVibrationAttributes(), externalVibration.getUid(),
                 // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
                 // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
@@ -82,24 +86,25 @@
     }
 
     @Override
-    public void linkToDeath(Runnable callback) {
-        synchronized (this) {
+    public boolean linkToDeath(Runnable callback) {
+        synchronized (mLock) {
             mBinderDeathCallback = callback;
         }
         mExternalVibration.linkToDeath(this);
+        return true;
     }
 
     @Override
     public void unlinkToDeath() {
         mExternalVibration.unlinkToDeath(this);
-        synchronized (this) {
+        synchronized (mLock) {
             mBinderDeathCallback = null;
         }
     }
 
     @Override
     public void binderDied() {
-        synchronized (this) {
+        synchronized (mLock) {
             if (mBinderDeathCallback != null) {
                 mBinderDeathCallback.run();
             }
@@ -119,9 +124,11 @@
     }
 
     @Override
-    public void notifyEnded() {
+    public void requestEnd(@NonNull Status status, @Nullable CallerInfo endedBy,
+            boolean immediate) {
         // Notify external client that this vibration should stop sending data to the vibrator.
         mExternalVibration.mute();
+        end(new EndInfo(status, endedBy));
     }
 
     boolean isHoldingSameVibration(ExternalVibration vib) {
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index ea4bd01..ce9c47b 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -36,6 +36,7 @@
 final class HalVibration extends Vibration {
 
     public final SparseArray<VibrationEffect> mFallbacks = new SparseArray<>();
+    public final IBinder callerToken;
 
     /** A {@link CountDownLatch} to enable waiting for completion. */
     private final CountDownLatch mCompletionLatch = new CountDownLatch(1);
@@ -55,9 +56,10 @@
     private int mScaleLevel;
     private float mAdaptiveScale;
 
-    HalVibration(@NonNull IBinder token, @NonNull CombinedVibration effect,
+    HalVibration(@NonNull IBinder callerToken, @NonNull CombinedVibration effect,
             @NonNull VibrationSession.CallerInfo callerInfo) {
-        super(token, callerInfo);
+        super(callerInfo);
+        this.callerToken = callerToken;
         mOriginalEffect = effect;
         mEffectToPlay = effect;
         mScaleLevel = VibrationScaler.SCALE_NONE;
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 9a04793..21fd4ce 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -53,16 +53,13 @@
     public final long id;
     public final VibrationSession.CallerInfo callerInfo;
     public final VibrationStats stats = new VibrationStats();
-    public final IBinder callerToken;
 
     private VibrationSession.Status mStatus;
 
-    Vibration(@NonNull IBinder token, @NonNull VibrationSession.CallerInfo callerInfo) {
-        Objects.requireNonNull(token);
+    Vibration(@NonNull VibrationSession.CallerInfo callerInfo) {
         Objects.requireNonNull(callerInfo);
         mStatus = VibrationSession.Status.RUNNING;
         this.id = sNextVibrationId.getAndIncrement();
-        this.callerToken = token;
         this.callerInfo = callerInfo;
     }
 
diff --git a/services/core/java/com/android/server/vibrator/VibrationSession.java b/services/core/java/com/android/server/vibrator/VibrationSession.java
index 5640b49..70477a2 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSession.java
@@ -49,14 +49,26 @@
      * Links this session to the app process death with given callback to handle it.
      *
      * <p>This can be used by the service to end the vibration session when the app process dies.
+     *
+     * @param callback The service callback to be triggered when the binder dies
+     * @return true if the link was successful, false otherwise
      */
-    void linkToDeath(Runnable callback);
+    boolean linkToDeath(@Nullable Runnable callback);
 
     /** Removes link to the app process death. */
     void unlinkToDeath();
 
-    /** Notify the session end was requested, which might be acted upon asynchronously. */
-    void notifyEnded();
+    /**
+     * Notify the session end was requested, which might be acted upon asynchronously.
+     *
+     * <p>Only the first end signal will be used to end a session, but subsequent calls with
+     * {@code immediate} flag set to true can still force it to take effect urgently.
+     *
+     * @param status the end status.
+     * @param endedBy the {@link CallerInfo} of the session that requested this session to end.
+     * @param immediate indicates whether cancellation should abort urgently and skip cleanup steps.
+     */
+    void requestEnd(@NonNull Status status, @Nullable CallerInfo endedBy, boolean immediate);
 
     /**
      * Session status with reference to values from vibratormanagerservice.proto for logging.
@@ -119,7 +131,7 @@
         public final String reason;
 
         CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
-                String reason) {
+                @Nullable String reason) {
             Objects.requireNonNull(attrs);
             this.attrs = attrs;
             this.uid = uid;
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 69cdcf4..9cb8c1a 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -31,6 +31,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.app.SynchronousUserSwitchObserver;
 import android.app.UidObserver;
 import android.content.BroadcastReceiver;
@@ -74,6 +75,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /** Controls all the system settings related to vibration. */
@@ -147,9 +149,6 @@
                     PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
                     PowerManager.GO_TO_SLEEP_REASON_TIMEOUT));
 
-    private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
-            new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
-
     /** Listener for changes on vibration settings. */
     interface OnVibratorSettingsChanged {
         /** Callback triggered when any of the vibrator settings change. */
@@ -158,15 +157,18 @@
 
     private final Object mLock = new Object();
     private final Context mContext;
-    private final String mSystemUiPackage;
     @VisibleForTesting
     final SettingsContentObserver mSettingObserver;
     @VisibleForTesting
-    final SettingsBroadcastReceiver mSettingChangeReceiver;
+    final RingerModeBroadcastReceiver mRingerModeBroadcastReceiver;
+    @VisibleForTesting
+    final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
     @VisibleForTesting
     final VibrationUidObserver mUidObserver;
     @VisibleForTesting
     final VibrationUserSwitchObserver mUserSwitchObserver;
+    @VisibleForTesting
+    final VibrationLowPowerModeListener mLowPowerModeListener;
 
     @GuardedBy("mLock")
     private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -180,10 +182,13 @@
     @GuardedBy("mLock")
     @Nullable
     private PowerManagerInternal mPowerManagerInternal;
+    @GuardedBy("mLock")
     @Nullable
     private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
 
     @GuardedBy("mLock")
+    private String mSystemUiPackage;
+    @GuardedBy("mLock")
     private boolean mVibrateInputDevices;
     @GuardedBy("mLock")
     private SparseIntArray mCurrentVibrationIntensities = new SparseIntArray();
@@ -205,11 +210,11 @@
         mContext = context;
         mVibrationConfig = config;
         mSettingObserver = new SettingsContentObserver(handler);
-        mSettingChangeReceiver = new SettingsBroadcastReceiver();
+        mRingerModeBroadcastReceiver = new RingerModeBroadcastReceiver();
+        mBatteryBroadcastReceiver = new BatteryBroadcastReceiver();
         mUidObserver = new VibrationUidObserver();
         mUserSwitchObserver = new VibrationUserSwitchObserver();
-        mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
-                .getSystemUiServiceComponent().getPackageName();
+        mLowPowerModeListener = new VibrationLowPowerModeListener();
 
         VibrationEffect clickEffect = createEffectFromResource(
                 com.android.internal.R.array.config_virtualKeyVibePattern);
@@ -233,18 +238,34 @@
     }
 
     public void onSystemReady() {
-        PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class);
-        AudioManager am = mContext.getSystemService(AudioManager.class);
-        int ringerMode = am.getRingerModeInternal();
+        onSystemReady(LocalServices.getService(PackageManagerInternal.class),
+                LocalServices.getService(PowerManagerInternal.class),
+                ActivityManager.getService(),
+                LocalServices.getService(VirtualDeviceManagerInternal.class),
+                mContext.getSystemService(AudioManager.class));
+    }
+
+    @VisibleForTesting
+    void onSystemReady(PackageManagerInternal packageManagerInternal,
+            PowerManagerInternal powerManagerInternal,
+            IActivityManager activityManagerInternal,
+            @Nullable VirtualDeviceManagerInternal virtualDeviceManagerInternal,
+            @Nullable AudioManager audioManager) {
+        int ringerMode = (audioManager == null)
+                ? AudioManager.RINGER_MODE_NORMAL
+                : audioManager.getRingerModeInternal();
+        String sysUiPackage = packageManagerInternal.getSystemUiServiceComponent().getPackageName();
 
         synchronized (mLock) {
-            mPowerManagerInternal = pm;
-            mAudioManager = am;
+            mPowerManagerInternal = powerManagerInternal;
+            mVirtualDeviceManagerInternal = virtualDeviceManagerInternal;
+            mAudioManager = audioManager;
             mRingerMode = ringerMode;
+            mSystemUiPackage = sysUiPackage;
         }
 
         try {
-            ActivityManager.getService().registerUidObserver(mUidObserver,
+            activityManagerInternal.registerUidObserver(mUidObserver,
                     ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
                     ActivityManager.PROCESS_STATE_UNKNOWN, /* callingPackage= */ null);
         } catch (RemoteException e) {
@@ -252,32 +273,16 @@
         }
 
         try {
-            ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+            activityManagerInternal.registerUserSwitchObserver(mUserSwitchObserver, TAG);
         } catch (RemoteException e) {
             // ignored; both services live in system_server
         }
 
-        pm.registerLowPowerModeObserver(
-                new PowerManagerInternal.LowPowerModeListener() {
-                    @Override
-                    public int getServiceType() {
-                        return PowerManager.ServiceType.VIBRATION;
-                    }
+        powerManagerInternal.registerLowPowerModeObserver(mLowPowerModeListener);
 
-                    @Override
-                    public void onLowPowerModeChanged(PowerSaveState result) {
-                        boolean shouldNotifyListeners;
-                        synchronized (mLock) {
-                            shouldNotifyListeners = result.batterySaverEnabled != mBatterySaverMode;
-                            mBatterySaverMode = result.batterySaverEnabled;
-                        }
-                        if (shouldNotifyListeners) {
-                            notifyListeners();
-                        }
-                    }
-                });
-
-        registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
+        mContext.registerReceiver(mRingerModeBroadcastReceiver,
+                new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION),
+                Context.RECEIVER_EXPORTED_UNAUDITED);
 
         // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
@@ -301,12 +306,7 @@
 
         if (mVibrationConfig.ignoreVibrationsOnWirelessCharger()) {
             Intent batteryStatus = mContext.registerReceiver(
-                    new BroadcastReceiver() {
-                        @Override
-                        public void onReceive(Context context, Intent intent) {
-                            updateBatteryInfo(intent);
-                        }
-                    },
+                    mBatteryBroadcastReceiver,
                     new IntentFilter(Intent.ACTION_BATTERY_CHANGED),
                     Context.RECEIVER_NOT_EXPORTED);
             // After registering the receiver for battery status, process the sticky broadcast that
@@ -476,8 +476,10 @@
     public boolean shouldCancelVibrationOnScreenOff(@NonNull CallerInfo callerInfo,
             long vibrationStartUptimeMillis) {
         PowerManagerInternal pm;
+        String sysUiPackageName;
         synchronized (mLock) {
             pm = mPowerManagerInternal;
+            sysUiPackageName = mSystemUiPackage;
         }
         if (pm != null) {
             // The SleepData from PowerManager may refer to a more recent sleep than the broadcast
@@ -501,7 +503,7 @@
         }
         // Only allow vibrations from System packages to continue vibrating when the screen goes off
         return callerInfo.uid != Process.SYSTEM_UID && callerInfo.uid != 0
-                && !mSystemUiPackage.equals(callerInfo.opPkg);
+                && !Objects.equals(sysUiPackageName, callerInfo.opPkg);
     }
 
     /**
@@ -782,11 +784,6 @@
                 UserHandle.USER_ALL);
     }
 
-    private void registerSettingsChangeReceiver(IntentFilter intentFilter) {
-        mContext.registerReceiver(mSettingChangeReceiver, intentFilter,
-                Context.RECEIVER_EXPORTED_UNAUDITED);
-    }
-
     @Nullable
     private VibrationEffect createEffectFromResource(int resId) {
         return createEffectFromResource(mContext.getResources(), resId);
@@ -833,12 +830,11 @@
     }
 
     private boolean isAppRunningOnAnyVirtualDevice(int uid) {
-        if (mVirtualDeviceManagerInternal == null) {
-            mVirtualDeviceManagerInternal =
-                    LocalServices.getService(VirtualDeviceManagerInternal.class);
+        VirtualDeviceManagerInternal vdm;
+        synchronized (mLock) {
+            vdm = mVirtualDeviceManagerInternal;
         }
-        return mVirtualDeviceManagerInternal != null
-                && mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(uid);
+        return vdm != null && vdm.isAppRunningOnAnyVirtualDevice(uid);
     }
 
     /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@@ -857,7 +853,7 @@
 
     /** Implementation of {@link BroadcastReceiver} to update on ringer mode change. */
     @VisibleForTesting
-    final class SettingsBroadcastReceiver extends BroadcastReceiver {
+    final class RingerModeBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -868,6 +864,18 @@
         }
     }
 
+    /** Implementation of {@link BroadcastReceiver} to update on battery mode change. */
+    @VisibleForTesting
+    final class BatteryBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+                updateBatteryInfo(intent);
+            }
+        }
+    }
+
     /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
     @VisibleForTesting
     final class VibrationUidObserver extends UidObserver {
@@ -913,4 +921,25 @@
             update();
         }
     }
+
+    /** Implementation of {@link PowerManagerInternal.LowPowerModeListener} for low battery. */
+    @VisibleForTesting
+    final class VibrationLowPowerModeListener implements PowerManagerInternal.LowPowerModeListener {
+        @Override
+        public int getServiceType() {
+            return PowerManager.ServiceType.VIBRATION;
+        }
+
+        @Override
+        public void onLowPowerModeChanged(PowerSaveState result) {
+            boolean shouldNotifyListeners;
+            synchronized (mLock) {
+                shouldNotifyListeners = result.batterySaverEnabled != mBatterySaverMode;
+                mBatterySaverMode = result.batterySaverEnabled;
+            }
+            if (shouldNotifyListeners) {
+                notifyListeners();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 5137d19..1d52e3c 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -21,6 +21,7 @@
 import android.os.Build;
 import android.os.CombinedVibration;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.VibrationEffect;
 import android.os.vibrator.Flags;
 import android.os.vibrator.PrebakedSegment;
@@ -38,6 +39,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.PriorityQueue;
 import java.util.Queue;
 import java.util.concurrent.CancellationException;
@@ -358,6 +360,28 @@
     }
 
     /**
+     * Returns true if successfully linked this conductor to the death of the binder that requested
+     * the vibration.
+     */
+    public boolean linkToDeath() {
+        try {
+            mVibration.callerToken.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error linking vibration to token death", e);
+            return false;
+        }
+        return true;
+    }
+
+    public void unlinkToDeath() {
+        try {
+            mVibration.callerToken.unlinkToDeath(this, 0);
+        } catch (NoSuchElementException e) {
+            Slog.wtf(TAG, "Failed to unlink vibration to token death", e);
+        }
+    }
+
+    /**
      * Notify the execution that cancellation is requested. This will be acted upon
      * asynchronously in the VibrationThread.
      *
@@ -452,6 +476,23 @@
         }
     }
 
+    /**
+     * Notify that the VibrationThread has completed the vibration effect playback.
+     *
+     * <p>This is a lightweight method intended to be called by the vibration thread directly. The
+     * VibrationThread may still be continuing with cleanup tasks, and should not be given new work
+     * until it notifies the manager that it has been released.
+     */
+    public void notifyVibrationComplete(@NonNull Vibration.EndInfo endInfo) {
+        if (Build.IS_DEBUGGABLE) {
+            expectIsVibrationThread(true);
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "Vibration " + mVibration.id + " finished with " + endInfo);
+        }
+        mVibration.end(endInfo);
+    }
+
     /** Returns true if a cancellation signal was sent via {@link #notifyCancelled}. */
     public boolean wasNotifiedToCancel() {
         if (Build.IS_DEBUGGABLE) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 4c1e16c..5b22c10 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -16,12 +16,12 @@
 
 package com.android.server.vibrator;
 
+import static android.os.Trace.TRACE_TAG_VIBRATOR;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.WorkSource;
@@ -31,7 +31,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.vibrator.VibrationSession.Status;
 
-import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /** Plays a {@link HalVibration} in dedicated thread. */
@@ -72,14 +71,6 @@
         void noteVibratorOff(int uid);
 
         /**
-         * Tell the manager that the currently active vibration has completed its vibration, from
-         * the perspective of the Effect. However, the VibrationThread may still be continuing with
-         * cleanup tasks, and should not be given new work until {@link #onVibrationThreadReleased}
-         * is called.
-         */
-        void onVibrationCompleted(long vibrationId, @NonNull Vibration.EndInfo vibrationEndInfo);
-
-        /**
          * Tells the manager that the VibrationThread is finished with the previous vibration and
          * all of its cleanup tasks, and the vibrators can now be used for another vibration.
          */
@@ -128,7 +119,7 @@
      *  before the release callback.
      */
     boolean runVibrationOnVibrationThread(VibrationStepConductor conductor) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrationOnVibrationThread");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "runVibrationOnVibrationThread");
         try {
             synchronized (mLock) {
                 if (mRequestedActiveConductor != null) {
@@ -140,7 +131,7 @@
             }
             return true;
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -243,7 +234,7 @@
         mWakeLock.acquire();
         try {
             try {
-                runCurrentVibrationWithWakeLockAndDeathLink();
+                playVibration();
             } finally {
                 clientVibrationCompleteIfNotAlready(
                         new Vibration.EndInfo(Status.FINISHED_UNEXPECTED));
@@ -254,46 +245,23 @@
         }
     }
 
-    /**
-     * Runs the VibrationThread with the binder death link, handling link/unlink failures.
-     * Called from within runWithWakeLock.
-     */
-    private void runCurrentVibrationWithWakeLockAndDeathLink() {
-        IBinder vibrationBinderToken = mExecutingConductor.getVibration().callerToken;
-        try {
-            vibrationBinderToken.linkToDeath(mExecutingConductor, 0);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Error linking vibration to token death", e);
-            clientVibrationCompleteIfNotAlready(
-                    new Vibration.EndInfo(Status.IGNORED_ERROR_TOKEN));
-            return;
-        }
-        // Ensure that the unlink always occurs now.
-        try {
-            // This is the actual execution of the vibration.
-            playVibration();
-        } finally {
-            try {
-                vibrationBinderToken.unlinkToDeath(mExecutingConductor, 0);
-            } catch (NoSuchElementException e) {
-                Slog.wtf(TAG, "Failed to unlink token", e);
-            }
-        }
-    }
-
     // Indicate that the vibration is complete. This can be called multiple times only for
     // convenience of handling error conditions - an error after the client is complete won't
     // affect the status.
     private void clientVibrationCompleteIfNotAlready(@NonNull Vibration.EndInfo vibrationEndInfo) {
         if (!mCalledVibrationCompleteCallback) {
             mCalledVibrationCompleteCallback = true;
-            mVibratorManagerHooks.onVibrationCompleted(
-                    mExecutingConductor.getVibration().id, vibrationEndInfo);
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "notifyVibrationComplete");
+            try {
+                mExecutingConductor.notifyVibrationComplete(vibrationEndInfo);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
+            }
         }
     }
 
     private void playVibration() {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "playVibration");
         try {
             mExecutingConductor.prepareToStart();
             while (!mExecutingConductor.isFinished()) {
@@ -317,7 +285,7 @@
                 }
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 3c47850..6aed00e 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vibrator;
 
+import static android.os.Trace.TRACE_TAG_VIBRATOR;
+
 import android.annotation.Nullable;
 import android.hardware.vibrator.IVibrator;
 import android.os.Binder;
@@ -55,8 +57,7 @@
     // for a snippet of the current known vibrator state/info.
     private volatile VibratorInfo mVibratorInfo;
     private volatile boolean mVibratorInfoLoadSuccessful;
-    private volatile boolean mIsVibrating;
-    private volatile boolean mIsUnderExternalControl;
+    private volatile VibratorState mCurrentState;
     private volatile float mCurrentAmplitude;
 
     /**
@@ -73,6 +74,11 @@
         void onComplete(int vibratorId, long vibrationId);
     }
 
+    /** Representation of the vibrator state based on the interactions through this controller. */
+    private enum VibratorState {
+        IDLE, VIBRATING, UNDER_EXTERNAL_CONTROL
+    }
+
     VibratorController(int vibratorId, OnVibrationCompleteListener listener) {
         this(vibratorId, listener, new NativeWrapper());
     }
@@ -85,6 +91,7 @@
         VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
         mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder);
         mVibratorInfo = vibratorInfoBuilder.build();
+        mCurrentState = VibratorState.IDLE;
 
         if (!mVibratorInfoLoadSuccessful) {
             Slog.e(TAG,
@@ -104,7 +111,7 @@
                     return false;
                 }
                 // Notify its callback after new client registered.
-                notifyStateListener(listener, mIsVibrating);
+                notifyStateListener(listener, isVibrating(mCurrentState));
             }
             return true;
         } finally {
@@ -124,7 +131,7 @@
 
     /** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
     public void reloadVibratorInfoIfNeeded() {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#reloadVibratorInfoIfNeeded");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#reloadVibratorInfoIfNeeded");
         try {
             // Early check outside lock, for quick return.
             if (mVibratorInfoLoadSuccessful) {
@@ -143,7 +150,7 @@
                 }
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -164,7 +171,7 @@
      * automatically notified to any registered {@link IVibratorStateListener} on change.
      */
     public boolean isVibrating() {
-        return mIsVibrating;
+        return isVibrating(mCurrentState);
     }
 
     /**
@@ -182,11 +189,6 @@
         return mCurrentAmplitude;
     }
 
-    /** Return {@code true} if this vibrator is under external control, false otherwise. */
-    public boolean isUnderExternalControl() {
-        return mIsUnderExternalControl;
-    }
-
     /**
      * Check against this vibrator capabilities.
      *
@@ -199,33 +201,37 @@
 
     /** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
     public boolean isAvailable() {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#isAvailable");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#isAvailable");
         try {
             synchronized (mLock) {
                 return mNativeWrapper.isAvailable();
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
     /**
      * Set the vibrator control to be external or not, based on given flag.
      *
-     * <p>This will affect the state of {@link #isUnderExternalControl()}.
+     * <p>This will affect the state of {@link #isVibrating()}.
      */
     public void setExternalControl(boolean externalControl) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "setExternalControl(" + externalControl + ")");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR,
+                externalControl ? "VibratorController#enableExternalControl"
+                : "VibratorController#disableExternalControl");
         try {
             if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
                 return;
             }
+            VibratorState newState =
+                    externalControl ? VibratorState.UNDER_EXTERNAL_CONTROL : VibratorState.IDLE;
             synchronized (mLock) {
-                mIsUnderExternalControl = externalControl;
                 mNativeWrapper.setExternalControl(externalControl);
+                updateStateAndNotifyListenersLocked(newState);
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -234,7 +240,7 @@
      * if given {@code effect} is {@code null}.
      */
     public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#updateAlwaysOn");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#updateAlwaysOn");
         try {
             if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
                 return;
@@ -248,24 +254,24 @@
                 }
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
     /** Set the vibration amplitude. This will NOT affect the state of {@link #isVibrating()}. */
     public void setAmplitude(float amplitude) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#setAmplitude");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#setAmplitude");
         try {
             synchronized (mLock) {
                 if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
                     mNativeWrapper.setAmplitude(amplitude);
                 }
-                if (mIsVibrating) {
+                if (mCurrentState == VibratorState.VIBRATING) {
                     mCurrentAmplitude = amplitude;
                 }
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -279,18 +285,18 @@
      * do not support the input or a negative number if the operation failed.
      */
     public long on(long milliseconds, long vibrationId) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on");
         try {
             synchronized (mLock) {
                 long duration = mNativeWrapper.on(milliseconds, vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
-                    notifyListenerOnVibrating(true);
+                    updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
                 }
                 return duration;
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -304,7 +310,7 @@
      * do not support the input or a negative number if the operation failed.
      */
     public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (vendor)");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (vendor)");
         synchronized (mLock) {
             Parcel vendorData = Parcel.obtain();
             try {
@@ -315,12 +321,12 @@
                         vendorEffect.getAdaptiveScale(), vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
-                    notifyListenerOnVibrating(true);
+                    updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
                 }
                 return duration;
             } finally {
                 vendorData.recycle();
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
     }
@@ -335,19 +341,19 @@
      * do not support the input or a negative number if the operation failed.
      */
     public long on(PrebakedSegment prebaked, long vibrationId) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (Prebaked)");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Prebaked)");
         try {
             synchronized (mLock) {
                 long duration = mNativeWrapper.perform(prebaked.getEffectId(),
                         prebaked.getEffectStrength(), vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
-                    notifyListenerOnVibrating(true);
+                    updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
                 }
                 return duration;
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -361,7 +367,7 @@
      * do not support the input or a negative number if the operation failed.
      */
     public long on(PrimitiveSegment[] primitives, long vibrationId) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (Primitive)");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Primitive)");
         try {
             if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
                 return 0;
@@ -370,12 +376,12 @@
                 long duration = mNativeWrapper.compose(primitives, vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
-                    notifyListenerOnVibrating(true);
+                    updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
                 }
                 return duration;
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -388,7 +394,7 @@
      * @return The duration of the effect playing, or 0 if unsupported.
      */
     public long on(RampSegment[] primitives, long vibrationId) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE)");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE)");
         try {
             if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
                 return 0;
@@ -398,12 +404,12 @@
                 long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
-                    notifyListenerOnVibrating(true);
+                    updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
                 }
                 return duration;
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -413,15 +419,15 @@
      * <p>This will affect the state of {@link #isVibrating()}.
      */
     public void off() {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#off");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#off");
         try {
             synchronized (mLock) {
                 mNativeWrapper.off();
                 mCurrentAmplitude = 0;
-                notifyListenerOnVibrating(false);
+                updateStateAndNotifyListenersLocked(VibratorState.IDLE);
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -439,9 +445,8 @@
         return "VibratorController{"
                 + "mVibratorInfo=" + mVibratorInfo
                 + ", mVibratorInfoLoadSuccessful=" + mVibratorInfoLoadSuccessful
-                + ", mIsVibrating=" + mIsVibrating
+                + ", mCurrentState=" + mCurrentState.name()
                 + ", mCurrentAmplitude=" + mCurrentAmplitude
-                + ", mIsUnderExternalControl=" + mIsUnderExternalControl
                 + ", mVibratorStateListeners count="
                 + mVibratorStateListeners.getRegisteredCallbackCount()
                 + '}';
@@ -450,8 +455,7 @@
     void dump(IndentingPrintWriter pw) {
         pw.println("Vibrator (id=" + mVibratorInfo.getId() + "):");
         pw.increaseIndent();
-        pw.println("isVibrating = " + mIsVibrating);
-        pw.println("isUnderExternalControl = " + mIsUnderExternalControl);
+        pw.println("currentState = " + mCurrentState.name());
         pw.println("currentAmplitude = " + mCurrentAmplitude);
         pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful);
         pw.println("vibratorStateListener size = "
@@ -460,14 +464,19 @@
         pw.decreaseIndent();
     }
 
+    /**
+     * Updates current vibrator state and notify listeners if {@link #isVibrating()} result changed.
+     */
     @GuardedBy("mLock")
-    private void notifyListenerOnVibrating(boolean isVibrating) {
-        if (mIsVibrating != isVibrating) {
-            mIsVibrating = isVibrating;
+    private void updateStateAndNotifyListenersLocked(VibratorState state) {
+        boolean previousIsVibrating = isVibrating(mCurrentState);
+        final boolean newIsVibrating = isVibrating(state);
+        mCurrentState = state;
+        if (previousIsVibrating != newIsVibrating) {
             // The broadcast method is safe w.r.t. register/unregister listener methods, but lock
             // is required here to guarantee delivery order.
             mVibratorStateListeners.broadcast(
-                    listener -> notifyStateListener(listener, isVibrating));
+                    listener -> notifyStateListener(listener, newIsVibrating));
         }
     }
 
@@ -479,6 +488,11 @@
         }
     }
 
+    /** Returns true only if given state is not {@link VibratorState#IDLE}. */
+    private static boolean isVibrating(VibratorState state) {
+        return state != VibratorState.IDLE;
+    }
+
     /** Wrapper around the static-native methods of {@link VibratorController} for tests. */
     @VisibleForTesting
     public static class NativeWrapper {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index a76d8d6..07473d1 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import static android.os.Trace.TRACE_TAG_VIBRATOR;
 import static android.os.VibrationAttributes.USAGE_CLASS_ALARM;
 import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
 import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
@@ -333,7 +334,7 @@
     @VisibleForTesting
     void systemReady() {
         Slog.v(TAG, "Initializing VibratorManager service...");
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "systemReady");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "systemReady");
         try {
             // Will retry to load each vibrator's info, if any request have failed.
             for (int i = 0; i < mVibrators.size(); i++) {
@@ -352,7 +353,7 @@
                 mServiceReady = true;
             }
             Slog.v(TAG, "VibratorManager service initialized");
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -413,7 +414,7 @@
     @Override // Binder call
     public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
             @Nullable CombinedVibration effect, @Nullable VibrationAttributes attrs) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "setAlwaysOnEffect");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "setAlwaysOnEffect");
         try {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.VIBRATE_ALWAYS_ON,
@@ -449,20 +450,25 @@
             }
             return true;
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
     @Override // Binder call
     public void vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect,
             @Nullable VibrationAttributes attrs, String reason, IBinder token) {
-        vibrateWithPermissionCheck(uid, deviceId, opPkg, effect, attrs, reason, token);
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "vibrate");
+        try {
+            vibrateWithPermissionCheck(uid, deviceId, opPkg, effect, attrs, reason, token);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
+        }
     }
 
     @Override // Binder call
     public void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
             String reason, int flags, int privFlags) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "performHapticFeedback");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedback");
         // Note that the `performHapticFeedback` method does not take a token argument from the
         // caller, and instead, uses this service as the token. This is to mitigate performance
         // impact that would otherwise be caused due to marshal latency. Haptic feedback effects are
@@ -471,7 +477,7 @@
             performHapticFeedbackInternal(uid, deviceId, opPkg, constant, reason, /* token= */
                     this, flags, privFlags);
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -479,13 +485,13 @@
     public void performHapticFeedbackForInputDevice(int uid, int deviceId, String opPkg,
             int constant, int inputDeviceId, int inputSource, String reason, int flags,
             int privFlags) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "performHapticFeedbackForInputDevice");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedbackForInputDevice");
         try {
             performHapticFeedbackForInputDeviceInternal(uid, deviceId, opPkg, constant,
                     inputDeviceId,
                     inputSource, reason, /* token= */ this, flags, privFlags);
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -563,26 +569,16 @@
     HalVibration vibrateWithPermissionCheck(int uid, int deviceId, String opPkg,
             @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
             String reason, IBinder token) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
-        try {
-            attrs = fixupVibrationAttributes(attrs, effect);
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.VIBRATE, "vibrate");
-            return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-        }
+        attrs = fixupVibrationAttributes(attrs, effect);
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.VIBRATE, "vibrate");
+        return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
     }
 
     HalVibration vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg,
             @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
             String reason, IBinder token) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
-        try {
-            return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-        }
+        return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token);
     }
 
     private HalVibration vibrateInternal(int uid, int deviceId, String opPkg,
@@ -633,12 +629,9 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mCurrentExternalVibration != null) {
-                        mCurrentExternalVibration.notifyEnded();
                         vib.stats.reportInterruptedAnotherVibration(
-                                mCurrentExternalVibration.callerInfo);
-                        endExternalVibrateLocked(
-                                new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
-                                        vib.callerInfo),
+                                mCurrentExternalVibration.getCallerInfo());
+                        endExternalVibrateLocked(Status.CANCELLED_SUPERSEDED, vib.callerInfo,
                                 /* continueExternalControl= */ false);
                     } else if (mCurrentVibration != null) {
                         if (mCurrentVibration.getVibration().canPipelineWith(vib)) {
@@ -666,7 +659,7 @@
 
             // Ignored or failed to start the vibration, end it and report metrics right away.
             if (vibrationEndInfo != null) {
-                endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ true);
+                endVibrationLocked(vib, vibrationEndInfo);
             }
             return vib;
         }
@@ -674,7 +667,7 @@
 
     @Override // Binder call
     public void cancelVibrate(int usageFilter, IBinder token) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelVibrate");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "cancelVibrate");
         try {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.VIBRATE,
@@ -703,16 +696,15 @@
                             && shouldCancelVibration(
                             mCurrentExternalVibration.getCallerInfo().attrs,
                             usageFilter)) {
-                        mCurrentExternalVibration.notifyEnded();
-                        endExternalVibrateLocked(
-                                cancelledByUserInfo, /* continueExternalControl= */ false);
+                        endExternalVibrateLocked(cancelledByUserInfo.status,
+                                cancelledByUserInfo.endedBy, /* continueExternalControl= */ false);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
             }
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -817,17 +809,9 @@
                 mCurrentExternalVibration.getDebugInfo().dump(proto,
                         VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
             }
-
-            boolean isVibrating = false;
-            boolean isUnderExternalControl = false;
             for (int i = 0; i < mVibrators.size(); i++) {
                 proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i));
-                isVibrating |= mVibrators.valueAt(i).isVibrating();
-                isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
             }
-            proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating);
-            proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
-                    isUnderExternalControl);
         }
         mVibratorManagerRecords.dump(proto);
         mVibratorControlService.dump(proto);
@@ -903,7 +887,7 @@
     @GuardedBy("mLock")
     @Nullable
     private Vibration.EndInfo startVibrationLocked(HalVibration vib) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
+        Trace.traceBegin(TRACE_TAG_VIBRATOR, "startVibrationLocked");
         try {
             if (mInputDeviceDelegate.isAvailable()) {
                 return startVibrationOnInputDevicesLocked(vib);
@@ -923,7 +907,7 @@
             mNextVibration = conductor;
             return null;
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            Trace.traceEnd(TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -934,9 +918,14 @@
         int mode = startAppOpModeLocked(vib.callerInfo);
         switch (mode) {
             case AppOpsManager.MODE_ALLOWED:
-                Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+                Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0);
                 // Make sure mCurrentVibration is set while triggering the VibrationThread.
                 mCurrentVibration = conductor;
+                if (!mCurrentVibration.linkToDeath()) {
+                    // Shouldn't happen. The method call already logs a wtf.
+                    mCurrentVibration = null;  // Aborted.
+                    return new Vibration.EndInfo(Status.IGNORED_ERROR_TOKEN);
+                }
                 if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) {
                     // Shouldn't happen. The method call already logs a wtf.
                     mCurrentVibration = null;  // Aborted.
@@ -953,14 +942,21 @@
     }
 
     @GuardedBy("mLock")
-    private void endVibrationLocked(Vibration vib, Vibration.EndInfo vibrationEndInfo,
-            boolean shouldWriteStats) {
+    private void endVibrationLocked(Vibration vib, Status status) {
+        endVibrationLocked(vib, new Vibration.EndInfo(status));
+    }
+
+    @GuardedBy("mLock")
+    private void endVibrationLocked(Vibration vib, Vibration.EndInfo vibrationEndInfo) {
         vib.end(vibrationEndInfo);
+        reportEndedVibrationLocked(vib);
+    }
+
+    @GuardedBy("mLock")
+    private void reportEndedVibrationLocked(Vibration vib) {
         logAndRecordVibration(vib.getDebugInfo());
-        if (shouldWriteStats) {
-            mFrameworkStatsLogger.writeVibrationReportedAsync(
-                    vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
-        }
+        mFrameworkStatsLogger.writeVibrationReportedAsync(
+                vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
     }
 
     private VibrationStepConductor createVibrationStepConductor(HalVibration vib) {
@@ -1055,17 +1051,15 @@
     }
 
     @GuardedBy("mLock")
-    private void reportFinishedVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
+    private void reportFinishedVibrationLocked() {
         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+        mCurrentVibration.unlinkToDeath();
         HalVibration vib = mCurrentVibration.getVibration();
         if (DEBUG) {
-            Slog.d(TAG, "Reporting vibration " + vib.id + " finished with "
-                    + vibrationEndInfo);
+            Slog.d(TAG, "Reporting vibration " + vib.id + " finished with " + vib.getStatus());
         }
-        // DO NOT write metrics at this point, wait for the VibrationThread to report the
-        // vibration was released, after all cleanup. The metrics will be reported then.
-        endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ false);
         finishAppOpModeLocked(vib.callerInfo);
+        reportEndedVibrationLocked(vib);
     }
 
     private void onSyncedVibrationComplete(long vibrationId) {
@@ -1575,7 +1569,7 @@
 
         @Override
         public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "prepareSyncedVibration");
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "prepareSyncedVibration");
             try {
                 if ((mCapabilities & requiredCapabilities) != requiredCapabilities) {
                     // This sync step requires capabilities this device doesn't have, skipping
@@ -1584,33 +1578,33 @@
                 }
                 return mNativeWrapper.prepareSynced(vibratorIds);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
         @Override
         public boolean triggerSyncedVibration(long vibrationId) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "triggerSyncedVibration");
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "triggerSyncedVibration");
             try {
                 return mNativeWrapper.triggerSynced(vibrationId);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
         @Override
         public void cancelSyncedVibration() {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelSyncedVibration");
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "cancelSyncedVibration");
             try {
                 mNativeWrapper.cancelSynced();
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
         @Override
         public void noteVibratorOn(int uid, long duration) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "noteVibratorOn");
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "noteVibratorOn");
             try {
                 if (duration <= 0) {
                     // Tried to turn vibrator ON and got:
@@ -1629,38 +1623,20 @@
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error logging VibratorStateChanged to ON", e);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
         @Override
         public void noteVibratorOff(int uid) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "noteVibratorOff");
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "noteVibratorOff");
             try {
                 mBatteryStatsService.noteVibratorOff(uid);
                 mFrameworkStatsLogger.writeVibratorStateOffAsync(uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error logging VibratorStateChanged to OFF", e);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-            }
-        }
-
-        @Override
-        public void onVibrationCompleted(long vibrationId, Vibration.EndInfo vibrationEndInfo) {
-            if (DEBUG) {
-                Slog.d(TAG, "Vibration " + vibrationId + " finished with " + vibrationEndInfo);
-            }
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onVibrationCompleted");
-            try {
-                synchronized (mLock) {
-                    if (mCurrentVibration != null
-                            && mCurrentVibration.getVibration().id == vibrationId) {
-                        reportFinishedVibrationLocked(vibrationEndInfo);
-                    }
-                }
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
@@ -1669,7 +1645,8 @@
             if (DEBUG) {
                 Slog.d(TAG, "VibrationThread released after finished vibration");
             }
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onVibrationThreadReleased: " + vibrationId);
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "onVibrationThreadReleased");
+
             try {
                 synchronized (mLock) {
                     if (DEBUG) {
@@ -1682,11 +1659,8 @@
                                 mCurrentVibration.getVibration().id, vibrationId));
                     }
                     if (mCurrentVibration != null) {
-                        // This is when we consider the current vibration complete, so report
-                        // metrics.
-                        mFrameworkStatsLogger.writeVibrationReportedAsync(
-                                mCurrentVibration.getVibration().getStatsInfo(
-                                        /* completionUptimeMillis= */ SystemClock.uptimeMillis()));
+                        // This is when we consider the current vibration complete, report metrics.
+                        reportFinishedVibrationLocked();
                         mCurrentVibration = null;
                     }
                     if (mNextVibration != null) {
@@ -1696,13 +1670,12 @@
                                 nextConductor);
                         if (vibrationEndInfo != null) {
                             // Failed to start the vibration, end it and report metrics right away.
-                            endVibrationLocked(nextConductor.getVibration(),
-                                    vibrationEndInfo, /* shouldWriteStats= */ true);
+                            endVibrationLocked(nextConductor.getVibration(), vibrationEndInfo);
                         }
                     }
                 }
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
     }
@@ -1929,8 +1902,7 @@
                         + " with end info: " + vibrationEndInfo);
             }
             // Clearing next vibration before playing it, end it and report metrics right away.
-            endVibrationLocked(mNextVibration.getVibration(), vibrationEndInfo,
-                    /* shouldWriteStats= */ true);
+            endVibrationLocked(mNextVibration.getVibration(), vibrationEndInfo);
             mNextVibration = null;
         }
     }
@@ -1938,23 +1910,24 @@
     /**
      * Ends the external vibration, and clears related service state.
      *
-     * @param vibrationEndInfo        the status and related info to end the associated Vibration
+     * @param status                  the status to end the associated Vibration
+     * @param endedBy                 the caller that caused this vibration to end
      * @param continueExternalControl indicates whether external control will continue. If not, the
      *                                HAL will have external control turned off.
      */
     @GuardedBy("mLock")
-    private void endExternalVibrateLocked(Vibration.EndInfo vibrationEndInfo,
+    private void endExternalVibrateLocked(Status status, CallerInfo endedBy,
             boolean continueExternalControl) {
         if (mCurrentExternalVibration == null) {
             return;
         }
+        mCurrentExternalVibration.requestEnd(status, endedBy, /* immediate= */ true);
         mCurrentExternalVibration.unlinkToDeath();
         if (!continueExternalControl) {
             setExternalControl(false, mCurrentExternalVibration.stats);
         }
         // The external control was turned off, end it and report metrics right away.
-        endVibrationLocked(mCurrentExternalVibration, vibrationEndInfo,
-                /* shouldWriteStats= */ true);
+        reportEndedVibrationLocked(mCurrentExternalVibration);
         mCurrentExternalVibration = null;
     }
 
@@ -2010,7 +1983,7 @@
 
         @Override
         public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onExternalVibrationStart");
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "onExternalVibrationStart");
             try {
                 // Create Vibration.Stats as close to the received request as possible, for
                 // tracking.
@@ -2022,9 +1995,7 @@
 
                 synchronized (mLock) {
                     if (!hasExternalControlCapability()) {
-                        endVibrationLocked(externalVibration,
-                                new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED),
-                                /* shouldWriteStats= */ true);
+                        endVibrationLocked(externalVibration, Status.IGNORED_UNSUPPORTED);
                         return externalVibration.getScale();
                     }
 
@@ -2035,9 +2006,7 @@
                         Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
                                 + " tried to play externally controlled vibration"
                                 + " without VIBRATE permission, ignoring.");
-                        endVibrationLocked(externalVibration,
-                                new Vibration.EndInfo(Status.IGNORED_MISSING_PERMISSION),
-                                /* shouldWriteStats= */ true);
+                        endVibrationLocked(externalVibration, Status.IGNORED_MISSING_PERMISSION);
                         return externalVibration.getScale();
                     }
 
@@ -2058,8 +2027,7 @@
                     }
 
                     if (vibrationEndInfo != null) {
-                        endVibrationLocked(externalVibration, vibrationEndInfo,
-                                /* shouldWriteStats= */ true);
+                        endVibrationLocked(externalVibration, vibrationEndInfo);
                         return externalVibration.getScale();
                     }
 
@@ -2092,15 +2060,32 @@
                         // as we would need to mute the old one still if it came from a different
                         // controller.
                         alreadyUnderExternalControl = true;
-                        mCurrentExternalVibration.notifyEnded();
                         externalVibration.stats.reportInterruptedAnotherVibration(
-                                mCurrentExternalVibration.callerInfo);
-                        endExternalVibrateLocked(
-                                new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
-                                        externalVibration.callerInfo),
-                                /* continueExternalControl= */ true);
+                                mCurrentExternalVibration.getCallerInfo());
+                        endExternalVibrateLocked(Status.CANCELLED_SUPERSEDED,
+                                externalVibration.callerInfo, /* continueExternalControl= */ true);
                     }
-
+                }
+                // Wait for lock and interact with HAL to set external control outside main lock.
+                if (waitForCompletion) {
+                    if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) {
+                        Slog.e(TAG, "Timed out waiting for vibration to cancel");
+                        synchronized (mLock) {
+                            endVibrationLocked(externalVibration, Status.IGNORED_ERROR_CANCELLING);
+                            return externalVibration.getScale();
+                        }
+                    }
+                }
+                if (!alreadyUnderExternalControl) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Vibrator going under external control.");
+                    }
+                    setExternalControl(true, externalVibration.stats);
+                }
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Playing external vibration: " + vib);
+                    }
                     VibrationAttributes attrs = fixupVibrationAttributes(
                             vib.getVibrationAttributes(),
                             /* effect= */ null);
@@ -2113,43 +2098,20 @@
                     mCurrentExternalVibration = externalVibration;
                     externalVibration.linkToDeath(this::onExternalVibrationBinderDied);
                     externalVibration.scale(mVibrationScaler, attrs.getUsage());
-                }
 
-                if (waitForCompletion) {
-                    if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) {
-                        Slog.e(TAG, "Timed out waiting for vibration to cancel");
-                        synchronized (mLock) {
-                            // Trigger endExternalVibrateLocked to unlink to death recipient.
-                            endExternalVibrateLocked(
-                                    new Vibration.EndInfo(Status.IGNORED_ERROR_CANCELLING),
-                                    /* continueExternalControl= */ false);
-                            // Mute the request, vibration will be ignored.
-                            externalVibration.muteScale();
-                        }
-                        return externalVibration.getScale();
-                    }
+                    // Vibrator will start receiving data from external channels after this point.
+                    // Report current time as the vibration start time, for debugging.
+                    externalVibration.stats.reportStarted();
+                    return externalVibration.getScale();
                 }
-                if (!alreadyUnderExternalControl) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Vibrator going under external control.");
-                    }
-                    setExternalControl(true, externalVibration.stats);
-                }
-                if (DEBUG) {
-                    Slog.d(TAG, "Playing external vibration: " + vib);
-                }
-                // Vibrator will start receiving data from external channels after this point.
-                // Report current time as the vibration start time, for debugging.
-                externalVibration.stats.reportStarted();
-                return externalVibration.getScale();
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
         @Override
         public void onExternalVibrationStop(ExternalVibration vib) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onExternalVibrationStop");
+            Trace.traceBegin(TRACE_TAG_VIBRATOR, "onExternalVibrationStop");
             try {
                 synchronized (mLock) {
                     if (mCurrentExternalVibration != null
@@ -2157,13 +2119,12 @@
                         if (DEBUG) {
                             Slog.d(TAG, "Stopping external vibration: " + vib);
                         }
-                        endExternalVibrateLocked(
-                                new Vibration.EndInfo(Status.FINISHED),
+                        endExternalVibrateLocked(Status.FINISHED, /* endedBy= */ null,
                                 /* continueExternalControl= */ false);
                     }
                 }
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
@@ -2182,8 +2143,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "External vibration finished because binder died");
                     }
-                    endExternalVibrateLocked(
-                            new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED),
+                    endExternalVibrateLocked(Status.CANCELLED_BINDER_DIED, /* endedBy= */ null,
                             /* continueExternalControl= */ false);
                 }
             }
@@ -2232,32 +2192,39 @@
 
         @Override
         public int onCommand(String cmd) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onCommand " + cmd);
             try {
                 if ("list".equals(cmd)) {
+                    Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: list");
                     return runListVibrators();
                 }
                 if ("synced".equals(cmd)) {
+                    Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: synced");
                     return runMono();
                 }
                 if ("combined".equals(cmd)) {
+                    Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: combined");
                     return runStereo();
                 }
                 if ("sequential".equals(cmd)) {
+                    Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: sequential");
                     return runSequential();
                 }
                 if ("xml".equals(cmd)) {
+                    Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: xml");
                     return runXml();
                 }
                 if ("cancel".equals(cmd)) {
+                    Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: cancel");
                     return runCancel();
                 }
                 if ("feedback".equals(cmd)) {
+                    Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: feedback");
                     return runHapticFeedback();
                 }
+                Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: default");
                 return handleDefaultCommands(cmd);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+                Trace.traceEnd(TRACE_TAG_VIBRATOR);
             }
         }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index e3e83b3..74ca230 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
@@ -171,63 +171,9 @@
             stream = new FileInputStream(file);
             TypedXmlPullParser parser = Xml.resolvePullParser(stream);
 
-            int type;
-            do {
-                type = parser.next();
-                if (type == XmlPullParser.START_TAG) {
-                    String tag = parser.getName();
-                    if (("wp".equals(tag) && loadSystem) || ("kwp".equals(tag) && loadLock)) {
-                        if ("kwp".equals(tag)) {
-                            lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
-                        }
-                        WallpaperData wallpaperToParse =
-                                "wp".equals(tag) ? wallpaper : lockWallpaper;
+            lockWallpaper = loadSettingsFromSerializer(parser, wallpaper, userId, loadSystem,
+                    loadLock, keepDimensionHints, wpdData);
 
-                        if (!multiCrop()) {
-                            parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
-                        }
-
-                        String comp = parser.getAttributeValue(null, "component");
-                        if (removeNextWallpaperComponent()) {
-                            wallpaperToParse.setComponent(comp != null
-                                    ? ComponentName.unflattenFromString(comp)
-                                    : null);
-                            if (wallpaperToParse.getComponent() == null
-                                    || "android".equals(wallpaperToParse.getComponent()
-                                    .getPackageName())) {
-                                wallpaperToParse.setComponent(mImageWallpaper);
-                            }
-                        } else {
-                            wallpaperToParse.nextWallpaperComponent = comp != null
-                                    ? ComponentName.unflattenFromString(comp)
-                                    : null;
-                            if (wallpaperToParse.nextWallpaperComponent == null
-                                    || "android".equals(wallpaperToParse.nextWallpaperComponent
-                                    .getPackageName())) {
-                                wallpaperToParse.nextWallpaperComponent = mImageWallpaper;
-                            }
-                        }
-
-                        if (multiCrop()) {
-                            parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
-                        }
-
-                        if (DEBUG) {
-                            Slog.v(TAG, "mWidth:" + wpdData.mWidth);
-                            Slog.v(TAG, "mHeight:" + wpdData.mHeight);
-                            Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
-                            Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
-                            Slog.v(TAG, "mName:" + wallpaper.name);
-                            if (removeNextWallpaperComponent()) {
-                                Slog.v(TAG, "mWallpaperComponent:" + wallpaper.getComponent());
-                            } else {
-                                Slog.v(TAG, "mNextWallpaperComponent:"
-                                        + wallpaper.nextWallpaperComponent);
-                            }
-                        }
-                    }
-                }
-            } while (type != XmlPullParser.END_DOCUMENT);
             success = true;
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "no current wallpaper -- first boot?");
@@ -275,6 +221,75 @@
         return new WallpaperLoadingResult(wallpaper, lockWallpaper, success);
     }
 
+    // This method updates `wallpaper` in place, but returns `lockWallpaper`. This is because
+    // `wallpaper` already exists if it's being read per `loadSystem`, but `lockWallpaper` is
+    // created conditionally if there is lock screen wallpaper data to read.
+    @VisibleForTesting
+    WallpaperData loadSettingsFromSerializer(TypedXmlPullParser parser, WallpaperData wallpaper,
+            int userId, boolean loadSystem, boolean loadLock, boolean keepDimensionHints,
+            DisplayData wpdData) throws IOException, XmlPullParserException {
+        WallpaperData lockWallpaper = null;
+        int type;
+        do {
+            type = parser.next();
+            if (type == XmlPullParser.START_TAG) {
+                String tag = parser.getName();
+                if (("wp".equals(tag) && loadSystem) || ("kwp".equals(tag) && loadLock)) {
+                    if ("kwp".equals(tag)) {
+                        lockWallpaper = new WallpaperData(userId, FLAG_LOCK);
+                    }
+                    WallpaperData wallpaperToParse =
+                            "wp".equals(tag) ? wallpaper : lockWallpaper;
+
+                    if (!multiCrop()) {
+                        parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
+                    }
+
+                    String comp = parser.getAttributeValue(null, "component");
+                    if (removeNextWallpaperComponent()) {
+                        wallpaperToParse.setComponent(comp != null
+                                ? ComponentName.unflattenFromString(comp)
+                                : null);
+                        if (wallpaperToParse.getComponent() == null
+                                || "android".equals(wallpaperToParse.getComponent()
+                                .getPackageName())) {
+                            wallpaperToParse.setComponent(mImageWallpaper);
+                        }
+                    } else {
+                        wallpaperToParse.nextWallpaperComponent = comp != null
+                                ? ComponentName.unflattenFromString(comp)
+                                : null;
+                        if (wallpaperToParse.nextWallpaperComponent == null
+                                || "android".equals(wallpaperToParse.nextWallpaperComponent
+                                .getPackageName())) {
+                            wallpaperToParse.nextWallpaperComponent = mImageWallpaper;
+                        }
+                    }
+
+                    if (multiCrop()) {
+                        parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
+                    }
+
+                    if (DEBUG) {
+                        Slog.v(TAG, "mWidth:" + wpdData.mWidth);
+                        Slog.v(TAG, "mHeight:" + wpdData.mHeight);
+                        Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
+                        Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
+                        Slog.v(TAG, "mName:" + wallpaper.name);
+                        if (removeNextWallpaperComponent()) {
+                            Slog.v(TAG, "mWallpaperComponent:" + wallpaper.getComponent());
+                        } else {
+                            Slog.v(TAG, "mNextWallpaperComponent:"
+                                    + wallpaper.nextWallpaperComponent);
+                        }
+                    }
+                }
+            }
+        } while (type != XmlPullParser.END_DOCUMENT);
+
+        return lockWallpaper;
+    }
+
     private void ensureSaneWallpaperData(WallpaperData wallpaper) {
         // Only overwrite cropHint if the rectangle is invalid.
         if (wallpaper.cropHint.width() < 0
@@ -449,18 +464,7 @@
         try {
             fstream = new FileOutputStream(journal.chooseForWrite(), false);
             TypedXmlSerializer out = Xml.resolveSerializer(fstream);
-            out.startDocument(null, true);
-
-            if (wallpaper != null) {
-                writeWallpaperAttributes(out, "wp", wallpaper);
-            }
-
-            if (lockWallpaper != null) {
-                writeWallpaperAttributes(out, "kwp", lockWallpaper);
-            }
-
-            out.endDocument();
-
+            saveSettingsToSerializer(out, wallpaper, lockWallpaper);
             fstream.flush();
             FileUtils.sync(fstream);
             fstream.close();
@@ -472,6 +476,22 @@
     }
 
     @VisibleForTesting
+    void saveSettingsToSerializer(TypedXmlSerializer out, WallpaperData wallpaper,
+            WallpaperData lockWallpaper) throws IOException {
+        out.startDocument(null, true);
+
+        if (wallpaper != null) {
+            writeWallpaperAttributes(out, "wp", wallpaper);
+        }
+
+        if (lockWallpaper != null) {
+            writeWallpaperAttributes(out, "kwp", lockWallpaper);
+        }
+
+        out.endDocument();
+    }
+
+    @VisibleForTesting
     void writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper)
             throws IllegalArgumentException, IllegalStateException, IOException {
         if (DEBUG) {
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 6740153..ab5316f 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -41,7 +41,8 @@
 
 import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
-import com.android.server.PinnerService;
+import com.android.server.pinner.PinnedFile;
+import com.android.server.pinner.PinnerService;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -318,8 +319,9 @@
             if (webviewPinQuota <= 0) {
                 break;
             }
-            int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
-            webviewPinQuota -= bytesPinned;
+            PinnedFile pf = pinnerService.pinFile(
+                    apk, webviewPinQuota, appInfo, PIN_GROUP, /*pinOptimizedDeps=*/true);
+            webviewPinQuota -= pf.bytesPinned;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ccc9b17..12d733f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2641,9 +2641,15 @@
             return true;
         }
         // Only do transfer after transaction has done when starting window exist.
-        if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
-            mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
-            return true;
+        if (mStartingData != null) {
+            final boolean isWaitingForSyncTransactionCommit =
+                    Flags.removeStartingWindowWaitForMultiTransitions()
+                            ? getSyncTransactionCommitCallbackDepth() > 0
+                            : mStartingData.mWaitForSyncTransactionCommit;
+            if (isWaitingForSyncTransactionCommit) {
+                mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
+                return true;
+            }
         }
         requestCopySplashScreen();
         return isTransferringSplashScreen();
@@ -2847,7 +2853,11 @@
         final boolean animate;
         final boolean hasImeSurface;
         if (mStartingData != null) {
-            if (mStartingData.mWaitForSyncTransactionCommit
+            final boolean isWaitingForSyncTransactionCommit =
+                    Flags.removeStartingWindowWaitForMultiTransitions()
+                            ? getSyncTransactionCommitCallbackDepth() > 0
+                            : mStartingData.mWaitForSyncTransactionCommit;
+            if (isWaitingForSyncTransactionCommit
                     || mSyncState != SYNC_STATE_NONE) {
                 mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
                 mStartingData.mPrepareRemoveAnimation = prepareAnimation;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bc11bac..d29ff54 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1749,12 +1749,13 @@
         mIntent.setFlags(mLaunchFlags);
 
         boolean dreamStopping = false;
-
-        for (ActivityRecord stoppingActivity : mSupervisor.mStoppingActivities) {
-            if (stoppingActivity.getActivityType()
-                    == WindowConfiguration.ACTIVITY_TYPE_DREAM) {
-                dreamStopping = true;
-                break;
+        if (!com.android.window.flags.Flags.removeActivityStarterDreamCallback()) {
+            for (ActivityRecord stoppingActivity : mSupervisor.mStoppingActivities) {
+                if (stoppingActivity.getActivityType()
+                        == WindowConfiguration.ACTIVITY_TYPE_DREAM) {
+                    dreamStopping = true;
+                    break;
+                }
             }
         }
 
@@ -1878,8 +1879,21 @@
         if (mDoResume) {
             if (!avoidMoveToFront()) {
                 mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
-                if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming()
-                        && !dreamStopping) {
+
+                final boolean launchBehindDream;
+                if (com.android.window.flags.Flags.removeActivityStarterDreamCallback()) {
+                    final TaskDisplayArea tda = mTargetRootTask.getTaskDisplayArea();
+                    final Task top = (tda != null ? tda.getTopRootTask() : null);
+                    launchBehindDream = (top != null && top != mTargetRootTask)
+                            && top.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_DREAM
+                            && top.getTopNonFinishingActivity() != null;
+                } else {
+                    launchBehindDream = !mTargetRootTask.isTopRootTaskInDisplayArea()
+                            && mService.isDreaming()
+                            && !dreamStopping;
+                }
+
+                if (launchBehindDream) {
                     // Launching underneath dream activity (fullscreen, always-on-top). Run the
                     // launch--behind transition so the Activity gets created and starts
                     // in visible state.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6009b4a..de95d1b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6706,6 +6706,14 @@
             synchronized (mGlobalLockWithoutBoost) {
                 mTaskSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo);
                 wpc.onConfigurationChanged(getGlobalConfiguration());
+                // Let the application initialize with consistent configuration as its activity.
+                for (int i = mStartingProcessActivities.size() - 1; i >= 0; i--) {
+                    final ActivityRecord r = mStartingProcessActivities.get(i);
+                    if (wpc.mUid == r.info.applicationInfo.uid && wpc.mName.equals(r.processName)) {
+                        wpc.registerActivityConfigurationListener(r);
+                        break;
+                    }
+                }
                 // The "info" can be the target of instrumentation.
                 return new PreBindInfo(compatibilityInfoForPackageLocked(info),
                         new Configuration(wpc.getConfiguration()));
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index 241390c1..fbf9478 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -170,7 +170,7 @@
      * </ul>
      */
     boolean shouldApplyFreeformTreatmentForCameraCompat() {
-        return Flags.cameraCompatForFreeform() && !isChangeEnabled(mActivityRecord,
+        return Flags.enableCameraCompatForDesktopWindowing() && !isChangeEnabled(mActivityRecord,
                 OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
     }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 67bfd76..5338c01 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -48,8 +48,9 @@
         // without the need to restart the device.
         final boolean needsDisplayRotationCompatPolicy =
                 wmService.mAppCompatConfiguration.isCameraCompatTreatmentEnabledAtBuildTime();
-        final boolean needsCameraCompatFreeformPolicy = Flags.cameraCompatForFreeform()
-                && DesktopModeHelper.canEnterDesktopMode(wmService.mContext);
+        final boolean needsCameraCompatFreeformPolicy =
+                Flags.enableCameraCompatForDesktopWindowing()
+                        && DesktopModeHelper.canEnterDesktopMode(wmService.mContext);
         if (needsDisplayRotationCompatPolicy || needsCameraCompatFreeformPolicy) {
             mCameraStateMonitor = new CameraStateMonitor(displayContent, wmService.mH);
             mActivityRefresher = new ActivityRefresher(wmService, wmService.mH);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 2259b5a..515f148 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -1120,7 +1120,9 @@
             @Nullable Task targetTask, int launchFlags, int balCode, int callingUid,
             int realCallingUid, TaskDisplayArea preferredTaskDisplayArea) {
         // BAL Exception allowed in all cases
-        if (balCode == BAL_ALLOW_ALLOWLISTED_UID) {
+        if (balCode == BAL_ALLOW_ALLOWLISTED_UID
+                || (android.security.Flags.asmReintroduceGracePeriod()
+                    && balCode == BAL_ALLOW_GRACE_PERIOD)) {
             return true;
         }
 
@@ -1173,10 +1175,15 @@
                 ArrayList<Task> visibleTasks = displayArea.getVisibleTasks();
                 for (int i = 0; i < visibleTasks.size(); i++) {
                     Task task = visibleTasks.get(i);
-                    if (visibleTasks.size() == 1 && task.isActivityTypeHomeOrRecents()) {
-                        bas.optedIn(task.getTopMostActivity());
-                    } else {
+                    if (android.security.Flags.asmReintroduceGracePeriod()) {
                         bas = checkTopActivityForAsm(task, callingUid, /*sourceRecord*/null, bas);
+                    } else {
+                        if (visibleTasks.size() == 1 && task.isActivityTypeHomeOrRecents()) {
+                            bas.optedIn(task.getTopMostActivity());
+                        } else {
+                            bas = checkTopActivityForAsm(
+                                task, callingUid, /*sourceRecord*/null, bas);
+                        }
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index e3232e0..d6caa1a 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -124,7 +124,7 @@
      */
     @VisibleForTesting
     boolean shouldApplyFreeformTreatmentForCameraCompat(@NonNull ActivityRecord activity) {
-        return Flags.cameraCompatForFreeform() && !activity.info.isChangeEnabled(
+        return Flags.enableCameraCompatForDesktopWindowing() && !activity.info.isChangeEnabled(
                 ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
     }
 
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 156d8a0..34b5f6a 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -103,7 +103,7 @@
         final TaskDisplayArea displayArea = task.getDisplayArea();
         final Rect screenBounds = displayArea.getBounds();
         final Size idealSize = calculateIdealSize(screenBounds, DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
-        if (!DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isEnabled()) {
+        if (!DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
             return centerInScreen(idealSize, screenBounds);
         }
         if (activity.mAppCompatController.getAppCompatAspectRatioOverrides()
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index da76317..b5ea0bd 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -36,7 +36,7 @@
 
     /** Whether desktop mode is enabled. */
     static boolean isDesktopModeEnabled() {
-        return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isEnabled();
+        return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0fa1a21..e6f6215 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2131,7 +2131,8 @@
         final DecorInsets.Info newInfo = mDecorInsets.mTmpInfo;
         final InsetsState newInsetsState = newInfo.update(mDisplayContent, rotation, dw, dh);
         final DecorInsets.Info currentInfo = getDecorInsetsInfo(rotation, dw, dh);
-        if (newInfo.mConfigFrame.equals(currentInfo.mConfigFrame)
+        final boolean sameConfigFrame = newInfo.mConfigFrame.equals(currentInfo.mConfigFrame);
+        if (sameConfigFrame
                 && newInfo.mOverrideConfigFrame.equals(currentInfo.mOverrideConfigFrame)) {
             // Even if the config frame is not changed in current rotation, it may change the
             // insets in other rotations if the frame of insets source is changed.
@@ -2155,7 +2156,7 @@
         }
         mDecorInsets.invalidate();
         mDecorInsets.mInfoForRotation[rotation].set(newInfo);
-        return true;
+        return !sameConfigFrame;
     }
 
     DecorInsets.Info getDecorInsetsInfo(int rotation, int w, int h) {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index e178203..8cc2fd1 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -132,15 +132,15 @@
     }
 
     @Override
-    protected boolean isLeashReadyForDispatching(InsetsControlTarget target) {
+    protected boolean isLeashReadyForDispatching() {
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
             final WindowState ws =
                     mWindowContainer != null ? mWindowContainer.asWindowState() : null;
             final boolean isDrawn = ws != null && ws.isDrawn();
-            return super.isLeashReadyForDispatching(target)
+            return super.isLeashReadyForDispatching()
                     && mServerVisible && isDrawn && mGivenInsetsReady;
         } else {
-            return super.isLeashReadyForDispatching(target);
+            return super.isLeashReadyForDispatching();
         }
     }
 
@@ -636,7 +636,7 @@
             sb.append(", leash is: ").append(hasLeash ? "non-null" : "null");
             if (!hasLeash) {
                 sb.append(", control is: ").append(mControl != null ? "non-null" : "null");
-                sb.append(", mIsLeashReadyForDispatching: ").append(mIsLeashReadyForDispatching);
+                sb.append(", mIsLeashInitialized: ").append(mIsLeashInitialized);
             }
             sb.append(", isImeLayeringTarget: ");
             sb.append(isImeLayeringTarget(mImeRequester, dcTarget));
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 232c3b6..dcf0319 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -188,9 +188,8 @@
      * the application did not handle.
      */
     @Override
-    public KeyEvent dispatchUnhandledKey(
-            IBinder focusedToken, KeyEvent event, int policyFlags) {
-        return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags);
+    public boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
+        return mService.mPolicy.interceptUnhandledKey(event, focusedToken);
     }
 
     /** Callback to get pointer layer. */
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index ddbfd70..d7dc459 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -222,7 +222,8 @@
             UserHandle clientUser) {
         final InputConsumerImpl existingConsumer = getInputConsumer(name);
         if (existingConsumer != null && existingConsumer.mClientUser.equals(clientUser)) {
-            throw new IllegalStateException("Existing input consumer found with name: " + name
+            destroyInputConsumer(existingConsumer.mToken);
+            Slog.w(TAG_WM, "Replacing existing input consumer found with name: " + name
                     + ", display: " + mDisplayId + ", user: " + clientUser);
         }
 
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f0a4763..4f8332a 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -71,11 +71,12 @@
     protected @Nullable WindowContainer mWindowContainer;
     protected @Nullable InsetsSourceControl mControl;
     protected @Nullable InsetsControlTarget mControlTarget;
-    protected boolean mIsLeashReadyForDispatching;
+    protected boolean mIsLeashInitialized;
 
     private final Rect mTmpRect = new Rect();
     private final InsetsSourceControl mFakeControl;
-    private final Consumer<Transaction> mSetLeashPositionConsumer;
+    private final Point mPosition = new Point();
+    private final Consumer<Transaction> mSetControlPositionConsumer;
     private @Nullable InsetsControlTarget mPendingControlTarget;
     private @Nullable InsetsControlTarget mFakeControlTarget;
 
@@ -126,13 +127,14 @@
                 source.getId(), source.getType(), null /* leash */, false /* initialVisible */,
                 new Point(), Insets.NONE);
         mControllable = (InsetsPolicy.CONTROLLABLE_TYPES & source.getType()) != 0;
-        mSetLeashPositionConsumer = t -> {
-            if (mControl != null) {
-                final SurfaceControl leash = mControl.getLeash();
-                if (leash != null) {
-                    final Point position = mControl.getSurfacePosition();
-                    t.setPosition(leash, position.x, position.y);
-                }
+        mSetControlPositionConsumer = t -> {
+            if (mControl == null || mControlTarget == null) {
+                return;
+            }
+            boolean changed = mControl.setSurfacePosition(mPosition.x, mPosition.y);
+            final SurfaceControl leash = mControl.getLeash();
+            if (changed && leash != null) {
+                t.setPosition(leash, mPosition.x, mPosition.y);
             }
             if (mHasPendingPosition) {
                 mHasPendingPosition = false;
@@ -140,9 +142,22 @@
                     mStateController.notifyControlTargetChanged(mPendingControlTarget, this);
                 }
             }
+            changed |= updateInsetsHint();
+            if (changed) {
+                mStateController.notifyControlChanged(mControlTarget, this);
+            }
         };
     }
 
+    private boolean updateInsetsHint() {
+        final Insets insetsHint = getInsetsHint();
+        if (!mControl.getInsetsHint().equals(insetsHint)) {
+            mControl.setInsetsHint(insetsHint);
+            return true;
+        }
+        return false;
+    }
+
     InsetsSource getSource() {
         return mSource;
     }
@@ -363,26 +378,32 @@
         }
         final boolean serverVisibleChanged = mServerVisible != isServerVisible;
         setServerVisible(isServerVisible);
-        updateInsetsControlPosition(windowState, serverVisibleChanged);
-    }
-
-    void updateInsetsControlPosition(WindowState windowState) {
-        updateInsetsControlPosition(windowState, false);
-    }
-
-    private void updateInsetsControlPosition(WindowState windowState,
-            boolean serverVisibleChanged) {
-        if (mControl == null) {
-            return;
+        final boolean positionChanged = updateInsetsControlPosition(windowState);
+        if (mControl != null && !positionChanged
+                // The insets hint would be updated if the position is changed. Here updates it for
+                // the possible change of the bounds or the server visibility.
+                && (updateInsetsHint()
+                        || serverVisibleChanged
+                                && android.view.inputmethod.Flags.refactorInsetsController())) {
+            // Only call notifyControlChanged here when the position is not changed. Otherwise, it
+            // is called or is scheduled to be called during updateInsetsControlPosition.
+            mStateController.notifyControlChanged(mControlTarget, this);
         }
-        boolean changed = false;
+    }
+
+    /**
+     * @return {#code true} if the surface position of the control is changed.
+     */
+    boolean updateInsetsControlPosition(WindowState windowState) {
+        if (mControl == null) {
+            return false;
+        }
         final Point position = getWindowFrameSurfacePosition();
-        if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
-            changed = true;
+        if (!mPosition.equals(position)) {
+            mPosition.set(position.x, position.y);
             if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
                     && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
-                mHasPendingPosition = true;
-                windowState.applyWithNextDraw(mSetLeashPositionConsumer);
+                windowState.applyWithNextDraw(mSetControlPositionConsumer);
             } else {
                 Transaction t = mWindowContainer.getSyncTransaction();
                 if (windowState != null) {
@@ -399,20 +420,11 @@
                         }
                     }
                 }
-                mSetLeashPositionConsumer.accept(t);
+                mSetControlPositionConsumer.accept(t);
             }
+            return true;
         }
-        final Insets insetsHint = getInsetsHint();
-        if (!mControl.getInsetsHint().equals(insetsHint)) {
-            mControl.setInsetsHint(insetsHint);
-            changed = true;
-        }
-        if (android.view.inputmethod.Flags.refactorInsetsController() && serverVisibleChanged) {
-            changed = true;
-        }
-        if (changed) {
-            mStateController.notifyControlChanged(mControlTarget, this);
-        }
+        return false;
     }
 
     private Point getWindowFrameSurfacePosition() {
@@ -553,8 +565,8 @@
                 ANIMATION_TYPE_INSETS_CONTROL);
 
         // The leash was just created. We cannot dispatch it until its surface transaction is
-        // applied. Otherwise, the client's operation to the leash might be overwritten by us.
-        mIsLeashReadyForDispatching = false;
+        // committed. Otherwise, the client's operation to the leash might be overwritten by us.
+        mIsLeashInitialized = false;
 
         final SurfaceControl leash = mAdapter.mCapturedLeash;
         mControlTarget = target;
@@ -590,7 +602,7 @@
      * @param id Indicates which transaction is committed so that stale callbacks can be dropped.
      */
     void onSurfaceTransactionCommitted(long id) {
-        if (mIsLeashReadyForDispatching) {
+        if (mIsLeashInitialized) {
             return;
         }
         if (mControl == null) {
@@ -599,7 +611,7 @@
         if (id != getSurfaceTransactionId(mControl.getLeash())) {
             return;
         }
-        mIsLeashReadyForDispatching = true;
+        mIsLeashInitialized = true;
         mStateController.notifySurfaceTransactionReady(this, 0, false);
     }
 
@@ -650,9 +662,12 @@
                 mServerVisible, mClientVisible);
     }
 
-    protected boolean isLeashReadyForDispatching(InsetsControlTarget target) {
-        // If the target is not the control target, we are ready for dispatching a null-leash to it.
-        return target != mControlTarget || mIsLeashReadyForDispatching;
+    protected boolean isLeashReadyForDispatching() {
+        return isLeashInitialized();
+    }
+
+    boolean isLeashInitialized() {
+        return mIsLeashInitialized;
     }
 
     /**
@@ -665,7 +680,7 @@
     @Nullable
     InsetsSourceControl getControl(InsetsControlTarget target) {
         if (target == mControlTarget) {
-            if (!isLeashReadyForDispatching(target) && mControl != null) {
+            if (!isLeashReadyForDispatching() && mControl != null) {
                 // The surface transaction of preparing leash is not applied yet. We don't send it
                 // to the client in case that the client applies its transaction sooner than ours
                 // that we could unexpectedly overwrite the surface state.
@@ -690,7 +705,7 @@
      */
     @Nullable
     protected SurfaceControl getLeash(@NonNull InsetsControlTarget target) {
-        return target == mControlTarget && mIsLeashReadyForDispatching && mControl != null
+        return target == mControlTarget && mIsLeashInitialized && mControl != null
                 ? mControl.getLeash() : null;
     }
 
@@ -739,7 +754,7 @@
             pw.println();
         }
         pw.print(prefix);
-        pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
+        pw.print("mIsLeashInitialized="); pw.print(mIsLeashInitialized);
         pw.print(" mHasPendingPosition="); pw.print(mHasPendingPosition);
         pw.println();
         if (mWindowContainer != null) {
@@ -785,7 +800,7 @@
         if (mAdapter != null && mAdapter.mCapturedLeash != null) {
             mAdapter.mCapturedLeash.dumpDebug(proto, CAPTURED_LEASH);
         }
-        proto.write(IS_LEASH_READY_FOR_DISPATCHING, mIsLeashReadyForDispatching);
+        proto.write(IS_LEASH_READY_FOR_DISPATCHING, isLeashReadyForDispatching());
         proto.write(CLIENT_VISIBLE, mClientVisible);
         proto.write(SERVER_VISIBLE, mServerVisible);
         proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 3e39a45..03fadba 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -420,7 +420,7 @@
                 final ArrayList<InsetsSourceProvider> providers = pendingControlMap.valueAt(i);
                 for (int p = providers.size() - 1; p >= 0; p--) {
                     final InsetsSourceProvider provider = providers.get(p);
-                    if (provider.isLeashReadyForDispatching(target)) {
+                    if (provider.isLeashInitialized() || provider.getControlTarget() != target) {
                         // Stop waiting for this provider.
                         providers.remove(p);
                     }
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 2394da9..4aa4f22 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
 import android.os.Environment;
+import android.os.Process;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -50,6 +51,9 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
 import java.util.function.IntFunction;
 
 /**
@@ -84,6 +88,12 @@
     private PackageList mPackageList;
 
     /**
+     * A map from user ID to the active {@link LoadingTask} when we're loading the launch params for
+     * that user.
+     */
+    private final SparseArray<LoadingTask> mLoadingTaskMap = new SparseArray<>();
+
+    /**
      * A dual layer map that first maps user ID to a secondary map, which maps component name (the
      * launching activity of tasks) to {@link PersistableLaunchParams} that stores launch metadata
      * that are stable across reboots.
@@ -117,113 +127,33 @@
     }
 
     void onUnlockUser(int userId) {
-        loadLaunchParams(userId);
-    }
-
-    void onCleanupUser(int userId) {
-        mLaunchParamsMap.remove(userId);
-    }
-
-    private void loadLaunchParams(int userId) {
-        final List<File> filesToDelete = new ArrayList<>();
-        final File launchParamsFolder = getLaunchParamFolder(userId);
-        if (!launchParamsFolder.isDirectory()) {
-            Slog.i(TAG, "Didn't find launch param folder for user " + userId);
+        if (mLoadingTaskMap.contains(userId)) {
+            Slog.e(TAG, "Duplicated onUnlockUser " + userId);
             return;
         }
 
-        final Set<String> packages = new ArraySet<>(mPackageList.getPackageNames());
+        final LoadingTask task = new LoadingTask(userId);
+        mLoadingTaskMap.put(userId, task);
+        task.execute();
+    }
 
-        final File[] paramsFiles = launchParamsFolder.listFiles();
-        final ArrayMap<ComponentName, PersistableLaunchParams> map =
-                new ArrayMap<>(paramsFiles.length);
+    void onCleanupUser(int userId) {
+        // There is no need to abort the task itself. Just let the loading task finish silently
+        // without modifying any state.
+        mLoadingTaskMap.remove(userId);
+        mLaunchParamsMap.remove(userId);
+    }
+
+    private void waitAndMoveResultIfLoading(int userId) {
+        final LoadingTask task = mLoadingTaskMap.removeReturnOld(userId);
+        if (task == null) {
+            return;
+        }
+        final ArrayMap<ComponentName, PersistableLaunchParams> map = task.get();
+        if (map == null) {
+            return;
+        }
         mLaunchParamsMap.put(userId, map);
-
-        for (File paramsFile : paramsFiles) {
-            if (!paramsFile.isFile()) {
-                Slog.w(TAG, paramsFile.getAbsolutePath() + " is not a file.");
-                continue;
-            }
-            if (!paramsFile.getName().endsWith(LAUNCH_PARAMS_FILE_SUFFIX)) {
-                Slog.w(TAG, "Unexpected params file name: " + paramsFile.getName());
-                filesToDelete.add(paramsFile);
-                continue;
-            }
-            String paramsFileName = paramsFile.getName();
-            // Migrate all records from old separator to new separator.
-            final int oldSeparatorIndex =
-                    paramsFileName.indexOf(OLD_ESCAPED_COMPONENT_SEPARATOR);
-            if (oldSeparatorIndex != -1) {
-                if (paramsFileName.indexOf(
-                        OLD_ESCAPED_COMPONENT_SEPARATOR, oldSeparatorIndex + 1) != -1) {
-                    // Rare case. We have more than one old escaped component separator probably
-                    // because this app uses underscore in their package name. We can't distinguish
-                    // which one is the real separator so let's skip it.
-                    filesToDelete.add(paramsFile);
-                    continue;
-                }
-                paramsFileName = paramsFileName.replace(
-                        OLD_ESCAPED_COMPONENT_SEPARATOR, ESCAPED_COMPONENT_SEPARATOR);
-                final File newFile = new File(launchParamsFolder, paramsFileName);
-                if (paramsFile.renameTo(newFile)) {
-                    paramsFile = newFile;
-                } else {
-                    // Rare case. For some reason we can't rename the file. Let's drop this record
-                    // instead.
-                    filesToDelete.add(paramsFile);
-                    continue;
-                }
-            }
-            final String componentNameString = paramsFileName.substring(
-                    0 /* beginIndex */,
-                    paramsFileName.length() - LAUNCH_PARAMS_FILE_SUFFIX.length())
-                    .replace(ESCAPED_COMPONENT_SEPARATOR, ORIGINAL_COMPONENT_SEPARATOR);
-            final ComponentName name = ComponentName.unflattenFromString(
-                    componentNameString);
-            if (name == null) {
-                Slog.w(TAG, "Unexpected file name: " + paramsFileName);
-                filesToDelete.add(paramsFile);
-                continue;
-            }
-
-            if (!packages.contains(name.getPackageName())) {
-                // Rare case. PersisterQueue doesn't have a chance to remove files for removed
-                // packages last time.
-                filesToDelete.add(paramsFile);
-                continue;
-            }
-
-            try (InputStream in = new FileInputStream(paramsFile)) {
-                final PersistableLaunchParams params = new PersistableLaunchParams();
-                final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-                int event;
-                while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && event != XmlPullParser.END_TAG) {
-                    if (event != XmlPullParser.START_TAG) {
-                        continue;
-                    }
-
-                    final String tagName = parser.getName();
-                    if (!TAG_LAUNCH_PARAMS.equals(tagName)) {
-                        Slog.w(TAG, "Unexpected tag name: " + tagName);
-                        continue;
-                    }
-
-                    params.restore(paramsFile, parser);
-                }
-
-                map.put(name, params);
-                addComponentNameToLaunchParamAffinityMapIfNotNull(
-                        name, params.mWindowLayoutAffinity);
-            } catch (Exception e) {
-                Slog.w(TAG, "Failed to restore launch params for " + name, e);
-                filesToDelete.add(paramsFile);
-            }
-        }
-
-        if (!filesToDelete.isEmpty()) {
-            mPersisterQueue.addItem(new CleanUpComponentQueueItem(filesToDelete), true);
-        }
     }
 
     void saveTask(Task task) {
@@ -236,6 +166,7 @@
             return;
         }
         final int userId = task.mUserId;
+        waitAndMoveResultIfLoading(userId);
         PersistableLaunchParams params;
         ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId);
         if (map == null) {
@@ -297,6 +228,7 @@
     void getLaunchParams(Task task, ActivityRecord activity, LaunchParams outParams) {
         final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
         final int userId = task != null ? task.mUserId : activity.mUserId;
+        waitAndMoveResultIfLoading(userId);
         final String windowLayoutAffinity;
         if (task != null) {
             windowLayoutAffinity = task.mWindowLayoutAffinity;
@@ -394,6 +326,137 @@
         }
     }
 
+    private class LoadingTask
+            implements Callable<ArrayMap<ComponentName, PersistableLaunchParams>> {
+        private final int mUserId;
+        private final FutureTask<ArrayMap<ComponentName, PersistableLaunchParams>> mFutureTask;
+
+        private LoadingTask(int userId) {
+            mUserId = userId;
+            mFutureTask = new FutureTask<>(this);
+        }
+
+        private void execute() {
+            new Thread(mFutureTask).start();
+        }
+
+        private ArrayMap<ComponentName, PersistableLaunchParams> get() {
+            try {
+                return mFutureTask.get();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            } catch (ExecutionException e) {
+                Slog.e(TAG, "Failed to load launch params for user#" + mUserId, e);
+                return null;
+            }
+        }
+
+        @Override
+        public ArrayMap<ComponentName, PersistableLaunchParams> call() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+            final List<File> filesToDelete = new ArrayList<>();
+            final File launchParamsFolder = getLaunchParamFolder(mUserId);
+            if (!launchParamsFolder.isDirectory()) {
+                Slog.i(TAG, "Didn't find launch param folder for user " + mUserId);
+                return null;
+            }
+
+            final Set<String> packages = new ArraySet<>(mPackageList.getPackageNames());
+
+            final File[] paramsFiles = launchParamsFolder.listFiles();
+            final ArrayMap<ComponentName, PersistableLaunchParams> map =
+                    new ArrayMap<>(paramsFiles.length);
+
+            for (File paramsFile : paramsFiles) {
+                if (!paramsFile.isFile()) {
+                    Slog.w(TAG, paramsFile.getAbsolutePath() + " is not a file.");
+                    continue;
+                }
+                if (!paramsFile.getName().endsWith(LAUNCH_PARAMS_FILE_SUFFIX)) {
+                    Slog.w(TAG, "Unexpected params file name: " + paramsFile.getName());
+                    filesToDelete.add(paramsFile);
+                    continue;
+                }
+                String paramsFileName = paramsFile.getName();
+                // Migrate all records from old separator to new separator.
+                final int oldSeparatorIndex =
+                        paramsFileName.indexOf(OLD_ESCAPED_COMPONENT_SEPARATOR);
+                if (oldSeparatorIndex != -1) {
+                    if (paramsFileName.indexOf(
+                            OLD_ESCAPED_COMPONENT_SEPARATOR, oldSeparatorIndex + 1) != -1) {
+                        // Rare case. We have more than one old escaped component separator probably
+                        // because this app uses underscore in their package name. We can't
+                        // distinguish which one is the real separator so let's skip it.
+                        filesToDelete.add(paramsFile);
+                        continue;
+                    }
+                    paramsFileName = paramsFileName.replace(
+                            OLD_ESCAPED_COMPONENT_SEPARATOR, ESCAPED_COMPONENT_SEPARATOR);
+                    final File newFile = new File(launchParamsFolder, paramsFileName);
+                    if (paramsFile.renameTo(newFile)) {
+                        paramsFile = newFile;
+                    } else {
+                        // Rare case. For some reason we can't rename the file. Let's drop this
+                        // record instead.
+                        filesToDelete.add(paramsFile);
+                        continue;
+                    }
+                }
+                final String componentNameString = paramsFileName.substring(
+                                0 /* beginIndex */,
+                                paramsFileName.length() - LAUNCH_PARAMS_FILE_SUFFIX.length())
+                        .replace(ESCAPED_COMPONENT_SEPARATOR, ORIGINAL_COMPONENT_SEPARATOR);
+                final ComponentName name = ComponentName.unflattenFromString(
+                        componentNameString);
+                if (name == null) {
+                    Slog.w(TAG, "Unexpected file name: " + paramsFileName);
+                    filesToDelete.add(paramsFile);
+                    continue;
+                }
+
+                if (!packages.contains(name.getPackageName())) {
+                    // Rare case. PersisterQueue doesn't have a chance to remove files for removed
+                    // packages last time.
+                    filesToDelete.add(paramsFile);
+                    continue;
+                }
+
+                try (InputStream in = new FileInputStream(paramsFile)) {
+                    final PersistableLaunchParams params = new PersistableLaunchParams();
+                    final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+                    int event;
+                    while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
+                            && event != XmlPullParser.END_TAG) {
+                        if (event != XmlPullParser.START_TAG) {
+                            continue;
+                        }
+
+                        final String tagName = parser.getName();
+                        if (!TAG_LAUNCH_PARAMS.equals(tagName)) {
+                            Slog.w(TAG, "Unexpected tag name: " + tagName);
+                            continue;
+                        }
+
+                        params.restore(paramsFile, parser);
+                    }
+
+                    map.put(name, params);
+                    addComponentNameToLaunchParamAffinityMapIfNotNull(
+                            name, params.mWindowLayoutAffinity);
+                } catch (Exception e) {
+                    Slog.w(TAG, "Failed to restore launch params for " + name, e);
+                    filesToDelete.add(paramsFile);
+                }
+            }
+
+            if (!filesToDelete.isEmpty()) {
+                mPersisterQueue.addItem(new CleanUpComponentQueueItem(filesToDelete), true);
+            }
+            return map;
+        }
+    }
+
     private class LaunchParamsWriteQueueItem
             implements PersisterQueue.WriteQueueItem<LaunchParamsWriteQueueItem> {
         private final int mUserId;
@@ -466,7 +529,7 @@
         }
     }
 
-    private class CleanUpComponentQueueItem implements PersisterQueue.WriteQueueItem {
+    private static class CleanUpComponentQueueItem implements PersisterQueue.WriteQueueItem {
         private final List<File> mComponentFiles;
 
         private CleanUpComponentQueueItem(List<File> componentFiles) {
@@ -483,7 +546,7 @@
         }
     }
 
-    private class PersistableLaunchParams {
+    private static class PersistableLaunchParams {
         private static final String ATTR_WINDOWING_MODE = "windowing_mode";
         private static final String ATTR_DISPLAY_UNIQUE_ID = "display_unique_id";
         private static final String ATTR_BOUNDS = "bounds";
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 7aede8b..bf623b2 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -24,6 +24,8 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -38,6 +40,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
@@ -1493,12 +1496,20 @@
             if (isExcludeFromRecents) {
                 if (DEBUG_RECENTS_TRIM_TASKS) {
                     Slog.d(TAG,
-                            "\texcludeFromRecents=true, taskIndex = " + taskIndex
-                                    + ", isOnHomeDisplay: " + task.isOnHomeDisplay());
+                            "\texcludeFromRecents=true,"
+                                + " taskIndex: " + taskIndex
+                                + " getTopVisibleActivity: " + task.getTopVisibleActivity()
+                                + " isOnHomeDisplay: " + task.isOnHomeDisplay());
                 }
                 // The Recents is only supported on default display now, we should only keep the
                 // most recent task of home display.
-                return (task.isOnHomeDisplay() && taskIndex == 0);
+                boolean isMostRecentTask;
+                if (enableRefactorTaskThumbnail()) {
+                    isMostRecentTask = task.getTopVisibleActivity() != null;
+                } else {
+                    isMostRecentTask = taskIndex == 0;
+                }
+                return (task.isOnHomeDisplay() && isMostRecentTask);
             }
         }
 
@@ -2031,10 +2042,15 @@
         final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED;
 
         // An activity type and windowing mode is compatible if they are the exact same type/mode,
-        // or if one of the type/modes is undefined
+        // or if one of the type/modes is undefined. This is with the exception of
+        // freeform/fullscreen where both modes are assumed to be compatible with each other.
         final boolean isCompatibleType = activityType == otherActivityType
                 || isUndefinedType || isOtherUndefinedType;
         final boolean isCompatibleMode = windowingMode == otherWindowingMode
+                || (windowingMode == WINDOWING_MODE_FREEFORM
+                && otherWindowingMode == WINDOWING_MODE_FULLSCREEN)
+                || (windowingMode == WINDOWING_MODE_FULLSCREEN
+                && otherWindowingMode == WINDOWING_MODE_FREEFORM)
                 || isUndefinedMode || isOtherUndefinedMode;
 
         return isCompatibleType && isCompatibleMode;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5550f3e..d295378 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -699,8 +699,10 @@
             final WindowState win = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (win != null) {
-                ImeTracker.forLogging().onProgress(imeStatsToken,
-                        ImeTracker.PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES);
+                if (android.view.inputmethod.Flags.refactorInsetsController()) {
+                    ImeTracker.forLogging().onProgress(imeStatsToken,
+                            ImeTracker.PHASE_WM_UPDATE_REQUESTED_VISIBLE_TYPES);
+                }
                 win.setRequestedVisibleTypes(requestedVisibleTypes);
                 win.getDisplayContent().getInsetsPolicy().onRequestedVisibleTypesChanged(win,
                         imeStatsToken);
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 24fb207..896612d 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -68,7 +68,9 @@
      * window.
      * Note this isn't equal to transition playing, the period should be
      * Sync finishNow -> Start transaction apply.
+     * @deprecated TODO(b/362347290): cleanup after fix ramp up
      */
+    @Deprecated
     boolean mWaitForSyncTransactionCommit;
 
     /**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a2fda0a..14f034b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -35,6 +35,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
+import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
@@ -49,6 +51,7 @@
 import static android.view.SurfaceControl.METADATA_TASK_ID;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
@@ -63,6 +66,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -133,6 +137,7 @@
 import android.app.PictureInPictureParams;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
+import android.app.compat.CompatChanges;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -503,6 +508,12 @@
     int mOffsetXForInsets;
     int mOffsetYForInsets;
 
+    /**
+     * Whether the compatibility overrides that change the resizability of the app should be allowed
+     * for the specific app.
+     */
+    boolean mAllowForceResizeOverride = true;
+
     private final AnimatingActivityRegistry mAnimatingActivityRegistry =
             new AnimatingActivityRegistry();
 
@@ -666,6 +677,7 @@
             intent = _intent;
             mMinWidth = minWidth;
             mMinHeight = minHeight;
+            updateAllowForceResizeOverride();
         }
         mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
         mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper);
@@ -1028,6 +1040,7 @@
             mTaskSupervisor.mRecentTasks.remove(this);
             mTaskSupervisor.mRecentTasks.add(this);
         }
+        updateAllowForceResizeOverride();
     }
 
     /** Sets the original minimal width and height. */
@@ -1823,6 +1836,17 @@
                 -1 /* don't check PID */, -1 /* don't check UID */, this);
     }
 
+    private void updateAllowForceResizeOverride() {
+        try {
+            mAllowForceResizeOverride = mAtmService.mContext.getPackageManager().getProperty(
+                    PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES,
+                    getBasePackageName()).getBoolean();
+        } catch (PackageManager.NameNotFoundException e) {
+            // Package not found or property not defined, reset to default value.
+            mAllowForceResizeOverride = true;
+        }
+    }
+
     /**
      * Check that a given bounds matches the application requested orientation.
      *
@@ -2812,7 +2836,18 @@
     boolean isResizeable(boolean checkPictureInPictureSupport) {
         final boolean forceResizable = mAtmService.mForceResizableActivities
                 && getActivityType() == ACTIVITY_TYPE_STANDARD;
-        return forceResizable || ActivityInfo.isResizeableMode(mResizeMode)
+        if (forceResizable) return true;
+
+        final UserHandle userHandle = UserHandle.getUserHandleForUid(mUserId);
+        final boolean forceResizableOverride = mAllowForceResizeOverride
+                && CompatChanges.isChangeEnabled(
+                        FORCE_RESIZE_APP, getBasePackageName(), userHandle);
+        final boolean forceNonResizableOverride = mAllowForceResizeOverride
+                && CompatChanges.isChangeEnabled(
+                        FORCE_NON_RESIZE_APP, getBasePackageName(), userHandle);
+
+        if (forceNonResizableOverride) return false;
+        return forceResizableOverride || ActivityInfo.isResizeableMode(mResizeMode)
                 || (mSupportsPictureInPicture && checkPictureInPictureSupport);
     }
 
@@ -6143,6 +6178,8 @@
 
     void maybeApplyLastRecentsAnimationTransaction() {
         if (mLastRecentsAnimationTransaction != null) {
+            ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+                    "Applying last recents animation transaction.");
             final SurfaceControl.Transaction tx = getPendingTransaction();
             if (mLastRecentsAnimationOverlay != null) {
                 tx.reparent(mLastRecentsAnimationOverlay, mSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 92953e5..83e714d 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -429,7 +429,7 @@
             }
 
             final IBinder activityToken;
-            if (activity.getPid() == mOrganizerPid) {
+            if (activity.getPid() == mOrganizerPid && activity.getUid() == mOrganizerUid) {
                 // We only pass the actual token if the activity belongs to the organizer process.
                 activityToken = activity.token;
             } else {
@@ -458,7 +458,8 @@
                 change.setTaskFragmentToken(lastParentTfToken);
             }
             // Only pass the activity token to the client if it belongs to the same process.
-            if (nextFillTaskActivity != null && nextFillTaskActivity.getPid() == mOrganizerPid) {
+            if (nextFillTaskActivity != null && nextFillTaskActivity.getPid() == mOrganizerPid
+                    && nextFillTaskActivity.getUid() == mOrganizerUid) {
                 change.setOtherActivityToken(nextFillTaskActivity.token);
             }
             return change;
@@ -553,6 +554,10 @@
                         "Replacing existing organizer currently unsupported");
             }
 
+            if (pid <= 0) {
+                throw new IllegalStateException("Cannot register from invalid pid: " + pid);
+            }
+
             if (restoreFromCachedStateIfPossible(organizer, pid, uid, outSavedState)) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0a47522..2451ca8 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1356,6 +1356,11 @@
                         mController.mAtm.setLastResumedActivityUncheckLocked(ar,
                                 "transitionFinished");
                     }
+
+                    // Prevent spurious background app switches.
+                    if (ar.mDisplayContent.mFocusedApp == ar) {
+                        mController.mAtm.stopAppSwitches();
+                    }
                 }
                 continue;
             }
@@ -1410,8 +1415,6 @@
             if (enterAutoPip) {
                 mController.mAtm.getTaskChangeNotificationController().notifyTaskStackChanged();
             }
-            // Prevent spurious background app switches.
-            mController.mAtm.stopAppSwitches();
             // The end of transient launch may not reorder task, so make sure to compute the latest
             // task rank according to the current visibility.
             mController.mAtm.mRootWindowContainer.rankTaskLayers();
@@ -3498,14 +3501,14 @@
         }
 
         boolean hasChanged() {
+            final boolean currVisible = mContainer.isVisibleRequested();
             // the task including transient launch must promote to root task
-            if ((mFlags & ChangeInfo.FLAG_TRANSIENT_LAUNCH) != 0
-                    || (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
+            if (currVisible && ((mFlags & ChangeInfo.FLAG_TRANSIENT_LAUNCH) != 0
+                    || (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0)) {
                 return true;
             }
             // If it's invisible and hasn't changed visibility, always return false since even if
             // something changed, it wouldn't be a visible change.
-            final boolean currVisible = mContainer.isVisibleRequested();
             if (currVisible == mVisible && !mVisible) return false;
             return currVisible != mVisible
                     || mKnownConfigChanges != 0
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7c3f0f2..1c03ba5 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -457,7 +457,7 @@
         source.setFrame(provider.getArbitraryRectangle())
                 .updateSideHint(getBounds())
                 .setBoundingRects(provider.getBoundingRects());
-        if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isEnabled()) {
+        if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()) {
             source.setFlags(provider.getFlags());
         }
         mLocalInsetsSources.put(id, source);
@@ -4351,4 +4351,7 @@
         t.merge(mSyncTransaction);
     }
 
+    int getSyncTransactionCommitCallbackDepth() {
+        return mSyncTransactionCommitCallbackDepth;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b8f47cc..9426347 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9007,7 +9007,9 @@
 
         final boolean isInputTargetNotFocused =
                 mFocusedInputTarget != t && mFocusedInputTarget != null;
-        if (!isInputTargetNotFocused) {
+        final boolean isTouchOnFocusedDisplay = mFocusedInputTarget != null
+                && t.getDisplayId() == mFocusedInputTarget.getDisplayId();
+        if (!(isInputTargetNotFocused && isTouchOnFocusedDisplay)) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 476443a..f35f2b3 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -799,7 +799,12 @@
             }
         } finally {
             if (deferTransitionReady) {
-                chain.mTransition.continueTransitionReady();
+                if (chain.mTransition.isCollecting()) {
+                    chain.mTransition.continueTransitionReady();
+                } else {
+                    Slog.wtf(TAG, "Too late, transition : " + chain.mTransition.getSyncId()
+                            + " state: " + chain.mTransition.getState() + " is not collecting");
+                }
             }
             mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
             if (deferResume) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 32fe303..2d9e848 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1543,6 +1543,11 @@
         unregisterConfigurationListeners();
         mConfigActivityRecord = activityRecord;
         activityRecord.registerConfigurationChangeListener(this);
+        // If the process hasn't attached, make sure that prepareConfigurationForLaunchingActivity
+        // will use the newer configuration sequence.
+        if (mThread == null) {
+            mHasPendingConfigurationChange = true;
+        }
     }
 
     private void unregisterActivityConfigurationListener() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1640ad3..0e3ab63 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1969,6 +1969,13 @@
     boolean isReadyForDisplay() {
         final boolean parentAndClientVisible = !isParentWindowHidden()
                 && mViewVisibility == View.VISIBLE;
+        // TODO(b/338426357): Remove this once the last target using legacy transitions is moved to
+        // shell transitions
+        if (!mTransitionController.isShellTransitionsEnabled()) {
+            return mHasSurface && isVisibleByPolicy() && !mDestroying
+                    && ((parentAndClientVisible && mToken.isVisible())
+                    || isAnimating(TRANSITION | PARENTS));
+        }
         return mHasSurface && isVisibleByPolicy() && !mDestroying && mToken.isVisible()
                 && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
     }
@@ -3856,16 +3863,8 @@
         }
         fillInsetsState(mLastReportedInsetsState, false /* copySources */);
         fillInsetsSourceControls(mLastReportedActiveControls, false /* copyControls */);
-        if (Flags.insetsControlChangedItem()) {
-            getProcess().scheduleClientTransactionItem(new WindowStateInsetsControlChangeItem(
-                    mClient, mLastReportedInsetsState, mLastReportedActiveControls));
-        } else {
-            try {
-                mClient.insetsControlChanged(mLastReportedInsetsState, mLastReportedActiveControls);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to deliver inset control state change to w=" + this, e);
-            }
-        }
+        getProcess().scheduleClientTransactionItem(new WindowStateInsetsControlChangeItem(
+                mClient, mLastReportedInsetsState, mLastReportedActiveControls));
     }
 
     @Override
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index d2493c5..efca902 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -56,6 +56,7 @@
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <server_configurable_flags/get_flags.h>
+#include <ui/LogicalDisplayId.h>
 #include <ui/Region.h>
 #include <utils/Log.h>
 #include <utils/Looper.h>
@@ -64,6 +65,7 @@
 
 #include <atomic>
 #include <cinttypes>
+#include <map>
 #include <vector>
 
 #include "android_hardware_display_DisplayViewport.h"
@@ -343,7 +345,7 @@
     void setTouchpadRightClickZoneEnabled(bool enabled);
     void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
     void setShowTouches(bool enabled);
-    void setInteractive(bool interactive);
+    void setNonInteractiveDisplays(const std::set<ui::LogicalDisplayId>& displayIds);
     void reloadCalibration();
     void reloadPointerIcons();
     void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled);
@@ -357,6 +359,7 @@
     FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId);
     void setStylusPointerIconEnabled(bool enabled);
     void setInputMethodConnectionIsActive(bool isActive);
+    void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -504,9 +507,14 @@
 
         // True if there is an active input method connection.
         bool isInputMethodConnectionActive{false};
+
+        // Keycodes to be remapped.
+        std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping{};
+
+        // Displays which are non-interactive.
+        std::set<ui::LogicalDisplayId> nonInteractiveDisplays;
     } mLocked GUARDED_BY(mLock);
 
-    std::atomic<bool> mInteractive;
     void updateInactivityTimeoutLocked();
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
     void ensureSpriteControllerLocked();
@@ -520,12 +528,13 @@
     void forEachPointerControllerLocked(std::function<void(PointerController&)> apply)
             REQUIRES(mLock);
     PointerIcon loadPointerIcon(JNIEnv* env, ui::LogicalDisplayId displayId, PointerIconStyle type);
+    bool isDisplayInteractive(ui::LogicalDisplayId displayId);
 
     static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); }
 };
 
 NativeInputManager::NativeInputManager(jobject serviceObj, const sp<Looper>& looper)
-      : mLooper(looper), mInteractive(true) {
+      : mLooper(looper) {
     JNIEnv* env = jniEnv();
 
     mServiceObj = env->NewGlobalRef(serviceObj);
@@ -543,9 +552,13 @@
 
 void NativeInputManager::dump(std::string& dump) {
     dump += "Input Manager State:\n";
-    dump += StringPrintf(INDENT "Interactive: %s\n", toString(mInteractive.load()));
     { // acquire lock
         std::scoped_lock _l(mLock);
+        auto logicalDisplayIdToString = [](const ui::LogicalDisplayId& displayId) {
+            return std::to_string(displayId.val());
+        };
+        dump += StringPrintf(INDENT "Display not interactive: %s\n",
+                             dumpSet(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);
@@ -761,6 +774,8 @@
         outConfig->stylusButtonMotionEventsEnabled = mLocked.stylusButtonMotionEventsEnabled;
 
         outConfig->stylusPointerIconEnabled = mLocked.stylusPointerIconEnabled;
+
+        outConfig->keyRemapping = mLocked.keyRemapping;
     } // release lock
 }
 
@@ -1470,8 +1485,10 @@
     mInputManager->getDispatcher().requestPointerCapture(windowToken, enabled);
 }
 
-void NativeInputManager::setInteractive(bool interactive) {
-    mInteractive = interactive;
+void NativeInputManager::setNonInteractiveDisplays(
+        const std::set<ui::LogicalDisplayId>& displayIds) {
+    std::scoped_lock _l(mLock);
+    mLocked.nonInteractiveDisplays = displayIds;
 }
 
 void NativeInputManager::reloadCalibration() {
@@ -1600,7 +1617,7 @@
     // - Ignore untrusted events and pass them along.
     // - Ask the window manager what to do with normal events and trusted injected events.
     // - For normal events wake and brighten the screen if currently off or dim.
-    const bool interactive = mInteractive.load();
+    const bool interactive = isDisplayInteractive(keyEvent.getDisplayId());
     if (interactive) {
         policyFlags |= POLICY_FLAG_INTERACTIVE;
     }
@@ -1638,7 +1655,7 @@
     // - No special filtering for injected events required at this time.
     // - Filter normal events based on screen state.
     // - For normal events brighten (but do not wake) the screen if currently dim.
-    const bool interactive = mInteractive.load();
+    const bool interactive = isDisplayInteractive(displayId);
     if (interactive) {
         policyFlags |= POLICY_FLAG_INTERACTIVE;
     }
@@ -1677,6 +1694,24 @@
     }
 }
 
+bool NativeInputManager::isDisplayInteractive(ui::LogicalDisplayId displayId) {
+    // If an input event doesn't have an associated id, use the default display id
+    if (displayId == ui::LogicalDisplayId::INVALID) {
+        displayId = ui::LogicalDisplayId::DEFAULT;
+    }
+
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        auto it = mLocked.nonInteractiveDisplays.find(displayId);
+        if (it != mLocked.nonInteractiveDisplays.end()) {
+            return false;
+        }
+    } // release lock
+
+    return true;
+}
+
 nsecs_t NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
                                                           const KeyEvent& keyEvent,
                                                           uint32_t policyFlags) {
@@ -1910,6 +1945,16 @@
     mInputManager->getDispatcher().setInputMethodConnectionIsActive(isActive);
 }
 
+void NativeInputManager::setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        mLocked.keyRemapping = keyRemapping;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::Change::KEY_REMAPPING);
+}
+
 // ----------------------------------------------------------------------------
 
 static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) {
@@ -1983,10 +2028,19 @@
     return vec;
 }
 
-static void nativeAddKeyRemapping(JNIEnv* env, jobject nativeImplObj, jint deviceId,
-                                  jint fromKeyCode, jint toKeyCode) {
+static void nativeSetKeyRemapping(JNIEnv* env, jobject nativeImplObj, jintArray fromKeyCodesArr,
+                                  jintArray toKeyCodesArr) {
+    const std::vector<int32_t> fromKeycodes = getIntArray(env, fromKeyCodesArr);
+    const std::vector<int32_t> toKeycodes = getIntArray(env, toKeyCodesArr);
+    if (fromKeycodes.size() != toKeycodes.size()) {
+        jniThrowRuntimeException(env, "FromKeycodes and toKeycodes cannot match.");
+    }
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
-    im->getInputManager()->getReader().addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
+    std::map<int32_t, int32_t> keyRemapping;
+    for (int i = 0; i < fromKeycodes.size(); i++) {
+        keyRemapping.insert_or_assign(fromKeycodes[i], toKeycodes[i]);
+    }
+    im->setKeyRemapping(keyRemapping);
 }
 
 static jboolean nativeHasKeys(JNIEnv* env, jobject nativeImplObj, jint deviceId, jint sourceMask,
@@ -2347,10 +2401,17 @@
     im->setShowTouches(enabled);
 }
 
-static void nativeSetInteractive(JNIEnv* env, jobject nativeImplObj, jboolean interactive) {
+static void nativeSetNonInteractiveDisplays(JNIEnv* env, jobject nativeImplObj,
+                                            jintArray displayIds) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
-    im->setInteractive(interactive);
+    const std::vector displayIdsVec = getIntArray(env, displayIds);
+    std::set<ui::LogicalDisplayId> logicalDisplayIds;
+    for (int displayId : displayIdsVec) {
+        logicalDisplayIds.emplace(ui::LogicalDisplayId{displayId});
+    }
+
+    im->setNonInteractiveDisplays(logicalDisplayIds);
 }
 
 static void nativeReloadCalibration(JNIEnv* env, jobject nativeImplObj) {
@@ -2491,7 +2552,7 @@
             jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
                                              gLightClassInfo.lightTypeKeyboardMicMute);
         } else {
-            ALOGW("Unknown light type %d", lightInfo.type);
+            ALOGW("Unknown light type %s", ftl::enum_string(lightInfo.type).c_str());
             continue;
         }
 
@@ -2955,7 +3016,7 @@
         {"getScanCodeState", "(III)I", (void*)nativeGetScanCodeState},
         {"getKeyCodeState", "(III)I", (void*)nativeGetKeyCodeState},
         {"getSwitchState", "(III)I", (void*)nativeGetSwitchState},
-        {"addKeyRemapping", "(III)V", (void*)nativeAddKeyRemapping},
+        {"setKeyRemapping", "([I[I)V", (void*)nativeSetKeyRemapping},
         {"hasKeys", "(II[I[Z)Z", (void*)nativeHasKeys},
         {"getKeyCodeForKeyLocation", "(II)I", (void*)nativeGetKeyCodeForKeyLocation},
         {"createInputChannel", "(Ljava/lang/String;)Landroid/view/InputChannel;",
@@ -2996,7 +3057,7 @@
          (void*)nativeSetShouldNotifyTouchpadHardwareState},
         {"setTouchpadRightClickZoneEnabled", "(Z)V", (void*)nativeSetTouchpadRightClickZoneEnabled},
         {"setShowTouches", "(Z)V", (void*)nativeSetShowTouches},
-        {"setInteractive", "(Z)V", (void*)nativeSetInteractive},
+        {"setNonInteractiveDisplays", "([I)V", (void*)nativeSetNonInteractiveDisplays},
         {"reloadCalibration", "()V", (void*)nativeReloadCalibration},
         {"vibrate", "(I[J[III)V", (void*)nativeVibrate},
         {"vibrateCombined", "(I[JLandroid/util/SparseArray;II)V", (void*)nativeVibrateCombined},
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
index cf96114..2836d46 100644
--- a/services/core/jni/com_android_server_utils_AnrTimer.cpp
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -19,6 +19,8 @@
 #include <sys/timerfd.h>
 #include <inttypes.h>
 #include <sys/stat.h>
+#include <unistd.h>
+#include <regex.h>
 
 #include <algorithm>
 #include <list>
@@ -26,6 +28,7 @@
 #include <set>
 #include <string>
 #include <vector>
+#include <map>
 
 #define LOG_TAG "AnrTimerService"
 #define ATRACE_TAG ATRACE_TAG_ACTIVITY_MANAGER
@@ -33,8 +36,8 @@
 
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-#include "core_jni_helpers.h"
+#include <android_runtime/AndroidRuntime.h>
+#include <core_jni_helpers.h>
 
 #include <processgroup/processgroup.h>
 #include <utils/Log.h>
@@ -109,32 +112,336 @@
 // Return the name of the process whose pid is the input.  If the process does not exist, the
 // name will "notfound".
 std::string getProcessName(pid_t pid) {
-    char buffer[PATH_MAX];
-    snprintf(buffer, sizeof(buffer), "/proc/%d/cmdline", pid);
-    int fd = ::open(buffer, O_RDONLY);
-    if (fd >= 0) {
-        size_t pos = 0;
-        ssize_t result;
-        while (pos < sizeof(buffer)-1) {
-            result = ::read(fd, buffer + pos, (sizeof(buffer) - pos) - 1);
-            if (result <= 0) {
-                break;
-            }
-        }
-        ::close(fd);
-
-        if (result >= 0) {
-            buffer[pos] = 0;
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+    FILE* cmdline = fopen(path, "r");
+    if (cmdline != nullptr) {
+        char name[PATH_MAX];
+        char const *retval = fgets(name, sizeof(name), cmdline);
+        fclose(cmdline);
+        if (retval == nullptr) {
+            return std::string("unknown");
         } else {
-            snprintf(buffer, sizeof(buffer), "err: %s", strerror(errno));
+            return std::string(name);
         }
     } else {
-        snprintf(buffer, sizeof(buffer), "notfound");
+        return std::string("notfound");
     }
-    return std::string(buffer);
 }
 
 /**
+ * Three wrappers of the trace utilities, which hard-code the timer track.
+ */
+void traceBegin(const char* msg, int cookie) {
+    ATRACE_ASYNC_FOR_TRACK_BEGIN(ANR_TIMER_TRACK, msg, cookie);
+}
+
+void traceEnd(int cookie) {
+    ATRACE_ASYNC_FOR_TRACK_END(ANR_TIMER_TRACK, cookie);
+}
+
+void traceEvent(const char* msg) {
+    ATRACE_INSTANT_FOR_TRACK(ANR_TIMER_TRACK, msg);
+}
+
+/**
+ * This class captures tracing information for processes tracked by an AnrTimer.  A user can
+ * configure tracing to have the AnrTimerService emit extra information for watched processes.
+ * singleton.
+ *
+ * The tracing configuration has two components: process selection and an optional early action.
+ *
+ *   Processes are selected in one of three ways:
+ *    1. A list of numeric linux process IDs.
+ *    2. A regular expression, matched against process names.
+ *    3. The keyword "all", to trace every process that uses an AnrTimer.
+ *   Perfetto trace events are always emitted for every operation on a traced process.
+ *
+ *   An early action occurs before the scheduled timeout.  The early timeout is specified as a
+ *   percentage (integer value in the range 0:100) of the programmed timeout.  The AnrTimer will
+ *   execute the early action at the early timeout.  The early action may terminate the timer.
+ *
+ *   There is one early action:
+ *    1. Expire - consider the AnrTimer expired and report it to the upper layers.
+ */
+class AnrTimerTracer {
+  public:
+    // Actions that can be taken when an early  timer expires.
+    enum EarlyAction {
+        // Take no action.  This is the value used when tracing is disabled.
+        None,
+        // Trace the timer but take no other action.
+        Trace,
+        // Report timer expiration to the upper layers.  This is terminal, in that
+        Expire,
+    };
+
+    // The trace information for a single timer.
+    struct TraceConfig {
+        bool enabled = false;
+        EarlyAction action = None;
+        int earlyTimeout = 0;
+    };
+
+    AnrTimerTracer() {
+        AutoMutex _l(lock_);
+        resetLocked();
+    }
+
+    // Return the TraceConfig for a process.
+    TraceConfig getConfig(int pid) {
+        AutoMutex _l(lock_);
+        // The most likely situation: no tracing is configured.
+        if (!config_.enabled) return {};
+        if (matchAllPids_) return config_;
+        if (watched_.contains(pid)) return config_;
+        if (!matchNames_) return {};
+        if (matchedPids_.contains(pid)) return config_;
+        if (unmatchedPids_.contains(pid)) return {};
+        std::string proc_name = getProcessName(pid);
+        bool matched = regexec(&regex_, proc_name.c_str(), 0, 0, 0) == 0;
+        if (matched) {
+            matchedPids_.insert(pid);
+            return config_;
+        } else {
+            unmatchedPids_.insert(pid);
+            return {};
+        }
+    }
+
+    // Set the trace configuration.  The input is a string that contains key/value pairs of the
+    // form "key=value".  Pairs are separated by spaces.  The function returns a string status.
+    // On success, the normalized config is returned.  On failure, the configuration reset the
+    // result contains an error message.  As a special case, an empty set of configs, or a
+    // config that contains only the keyword "show", will do nothing except return the current
+    // configuration.  On any error, all tracing is disabled.
+    std::pair<bool, std::string> setConfig(const std::vector<std::string>& config) {
+        AutoMutex _l(lock_);
+        if (config.size() == 0) {
+            // Implicit "show"
+            return { true, currentConfigLocked() };
+        } else if (config.size() == 1) {
+            // Process the one-word commands
+            const char* s = config[0].c_str();
+            if (strcmp(s, "show") == 0) {
+                return { true, currentConfigLocked() };
+            } else if (strcmp(s, "off") == 0) {
+                resetLocked();
+                return { true, currentConfigLocked() };
+            } else if (strcmp(s, "help") == 0) {
+                return { true, help() };
+            }
+        } else if (config.size() > 2) {
+            return { false, "unexpected values in config" };
+        }
+
+        // Barring an error in the remaining specification list, tracing will be enabled.
+        resetLocked();
+        // Fetch the process specification.  This must be the first configuration entry.
+        {
+            auto result = setTracedProcess(config[0]);
+            if (!result.first) return result;
+        }
+
+        // Process optional actions.
+        if (config.size() > 1) {
+            auto result = setTracedAction(config[1]);
+            if (!result.first) return result;
+        }
+
+        // Accept the result.
+        config_.enabled = true;
+        return { true, currentConfigLocked() };
+    }
+
+  private:
+    // Identify the processes to be traced.
+    std::pair<bool, std::string> setTracedProcess(std::string config) {
+        const char* s = config.c_str();
+        const char* word = nullptr;
+
+        if (strcmp(s, "pid=all") == 0) {
+            matchAllPids_ = true;
+        } else if ((word = startsWith(s, "pid=")) != nullptr) {
+            int p;
+            int n;
+            while (sscanf(word, "%d%n", &p, &n) == 1) {
+                watched_.insert(p);
+                word += n;
+                if (*word == ',') word++;
+            }
+            if (*word != 0) {
+                return { false, "invalid pid list" };
+            }
+            config_.action = Trace;
+        } else if ((word = startsWith(s, "name=")) != nullptr) {
+            if (matchNames_) {
+                regfree(&regex_);
+                matchNames_ = false;
+            }
+            if (regcomp(&regex_, word, REG_EXTENDED) != 0) {
+                return { false, "invalid regex" };
+            }
+            matchNames_ = true;
+            namePattern_ = word;
+            config_.action = Trace;
+        } else {
+            return { false, "no process specified" };
+        }
+        return { true, "" };
+    }
+
+    // Set the action to be taken on a traced process.  The incoming default action is Trace;
+    // this method may overwrite that action.
+    std::pair<bool, std::string> setTracedAction(std::string config) {
+        const char* s = config.c_str();
+        const char* word = nullptr;
+        if (sscanf(s, "expire=%d", &config_.earlyTimeout) == 1) {
+            if (config_.earlyTimeout < 0) {
+                return { false, "invalid expire timeout" };
+            }
+            config_.action = Expire;
+        } else {
+            return { false, std::string("cannot parse action ") + s };
+        }
+        return { true, "" };
+    }
+
+    // Return the string value of an action.
+    static const char* toString(EarlyAction action) {
+        switch (action) {
+            case None: return "none";
+            case Trace: return "trace";
+            case Expire: return "expire";
+        }
+        return "unknown";
+    }
+
+    // Return the action represented by the string.
+    static EarlyAction fromString(const char* action) {
+        if (strcmp(action, "expire") == 0) return Expire;
+        return None;
+    }
+
+    // Return the help message.  This has everything except the invocation command.
+    static std::string help() {
+        static const char* msg =
+                "help     show this message\n"
+                "show     report the current configuration\n"
+                "off      clear the current configuration, turning off all tracing\n"
+                "spec...  configure tracing according to the specification list\n"
+                "  action=<action>     what to do when a split timer expires\n"
+                "    expire            expire the timer to the upper levels\n"
+                "    event             generate extra trace events\n"
+                "  pid=<pid>[,<pid>]   watch the processes in the pid list\n"
+                "  pid=all             watch every process in the system\n"
+                "  name=<regex>        watch the processes whose name matches the regex\n";
+        return msg;
+    }
+
+    // A small convenience function for parsing.  If the haystack starts with the needle and the
+    // haystack has at least one more character following, return a pointer to the following
+    // character.  Otherwise return null.
+    static const char* startsWith(const char* haystack, const char* needle) {
+        if (strncmp(haystack, needle, strlen(needle)) == 0 && strlen(haystack) + strlen(needle)) {
+            return haystack + strlen(needle);
+        }
+        return nullptr;
+    }
+
+    // Return the currently watched pids.  The lock must be held.
+    std::string watchedPidsLocked() const {
+        if (watched_.size() == 0) return "none";
+        bool first = true;
+        std::string result = "";
+        for (auto i = watched_.cbegin(); i != watched_.cend(); i++) {
+            if (first) {
+                result += StringPrintf("%d", *i);
+            } else {
+                result += StringPrintf(",%d", *i);
+            }
+        }
+        return result;
+    }
+
+    // Return the current configuration, in a form that can be consumed by setConfig().
+    std::string currentConfigLocked() const {
+        if (!config_.enabled) return "off";
+        std::string result;
+        if (matchAllPids_) {
+            result = "pid=all";
+        } else if (matchNames_) {
+            result = StringPrintf("name=\"%s\"", namePattern_.c_str());
+        } else {
+            result = std::string("pid=") + watchedPidsLocked();
+        }
+        switch (config_.action) {
+            case None:
+                break;
+            case Trace:
+                // The default action is Trace
+                break;
+            case Expire:
+                result += StringPrintf(" %s=%d", toString(config_.action), config_.earlyTimeout);
+                break;
+        }
+        return result;
+    }
+
+    // Reset the current configuration.
+    void resetLocked() {
+        if (!config_.enabled) return;
+
+        config_.enabled = false;
+        config_.earlyTimeout = 0;
+        config_.action = {};
+        matchAllPids_ = false;
+        watched_.clear();
+        if (matchNames_) regfree(&regex_);
+        matchNames_ = false;
+        namePattern_ = "";
+        matchedPids_.clear();
+        unmatchedPids_.clear();
+    }
+
+    // The lock for all operations
+    mutable Mutex lock_;
+
+    // The current tracing information, when a process matches.
+    TraceConfig config_;
+
+    // A short-hand flag that causes all processes to be tracing without the overhead of
+    // searching any of the maps.
+    bool matchAllPids_;
+
+    // A set of process IDs that should be traced.  This is updated directly in setConfig()
+    // and only includes pids that were explicitly called out in the configuration.
+    std::set<pid_t> watched_;
+
+    // Name mapping is a relatively expensive operation, since the process name must be fetched
+    // from the /proc file system and then a regex must be evaluated.  However, name mapping is
+    // useful to ensure processes are traced at the moment they start.  To make this faster, a
+    // process's name is matched only once, and the result is stored in the matchedPids_ or
+    // unmatchedPids_ set, as appropriate.  This can lead to confusion if a process changes its
+    // name after it starts.
+
+    // The global flag that enables name matching.  If this is disabled then all name matching
+    // is disabled.
+    bool matchNames_;
+
+    // The regular expression that matches processes to be traced.  This is saved for logging.
+    std::string namePattern_;
+
+    // The compiled regular expression.
+    regex_t regex_;
+
+    // The set of all pids that whose process names match (or do not match) the name regex.
+    // There is one set for pids that match and one set for pids that do not match.
+    std::set<pid_t> matchedPids_;
+    std::set<pid_t> unmatchedPids_;
+};
+
+/**
  * This class encapsulates the anr timer service.  The service manages a list of individual
  * timers.  A timer is either Running or Expired.  Once started, a timer may be canceled or
  * accepted.  Both actions collect statistics about the timer and then delete it.  An expired
@@ -177,7 +484,7 @@
      * traditional void* and Java object pointer.  The remaining parameters are
      * configuration options.
      */
-    AnrTimerService(char const* label, notifier_t notifier, void* cookie, jweak jtimer, Ticker*,
+    AnrTimerService(const char* label, notifier_t notifier, void* cookie, jweak jtimer, Ticker*,
                     bool extend, bool freeze);
 
     // Delete the service and clean up memory.
@@ -211,6 +518,11 @@
     // Release a timer.  The timer must be in the expired list.
     bool release(timer_id_t);
 
+    // Configure a trace specification to trace selected timers.  See AnrTimerTracer for details.
+    static std::pair<bool, std::string> trace(const std::vector<std::string>& spec) {
+        return tracer_.setConfig(spec);
+    }
+
     // Return the Java object associated with this instance.
     jweak jtimer() const {
         return notifierObject_;
@@ -221,7 +533,7 @@
 
   private:
     // The service cannot be copied.
-    AnrTimerService(AnrTimerService const&) = delete;
+    AnrTimerService(const AnrTimerService&) = delete;
 
     // Insert a timer into the running list.  The lock must be held by the caller.
     void insertLocked(const Timer&);
@@ -230,7 +542,7 @@
     Timer removeLocked(timer_id_t timerId);
 
     // Add a timer to the expired list.
-    void addExpiredLocked(Timer const&);
+    void addExpiredLocked(const Timer&);
 
     // Scrub the expired list by removing all entries for non-existent processes.  The expired
     // lock must be held by the caller.
@@ -240,10 +552,10 @@
     static const char* statusString(Status);
 
     // The name of this service, for logging.
-    std::string const label_;
+    const std::string label_;
 
     // The callback that is invoked when a timer expires.
-    notifier_t const notifier_;
+    const notifier_t notifier_;
 
     // The two cookies passed to the notifier.
     void* notifierCookie_;
@@ -289,8 +601,13 @@
 
     // The clock used by this AnrTimerService.
     Ticker *ticker_;
+
+    // The global tracing specification.
+    static AnrTimerTracer tracer_;
 };
 
+AnrTimerTracer AnrTimerService::tracer_;
+
 class AnrTimerService::ProcessStats {
   public:
     nsecs_t cpu_time;
@@ -337,14 +654,23 @@
 class AnrTimerService::Timer {
   public:
     // A unique ID assigned when the Timer is created.
-    timer_id_t const id;
+    const timer_id_t id;
 
     // The creation parameters.  The timeout is the original, relative timeout.
-    int const pid;
-    int const uid;
-    nsecs_t const timeout;
-    bool const extend;
-    bool const freeze;
+    const int pid;
+    const int uid;
+    const nsecs_t timeout;
+    // True if the timer may be extended.
+    const bool extend;
+    // True if process should be frozen when its timer expires.
+    const bool freeze;
+    // This is a percentage between 0 and 100.  If it is non-zero then timer will fire at
+    // timeout*split/100, and the EarlyAction will be invoked.  The timer may continue running
+    // or may expire, depending on the action.  Thus, this value "splits" the timeout into two
+    // pieces.
+    const int split;
+    // The action to take if split (above) is non-zero, when the timer reaches the split point.
+    const AnrTimerTracer::EarlyAction action;
 
     // The state of this timer.
     Status status;
@@ -355,6 +681,9 @@
     // The scheduled timeout.  This is an absolute time.  It may be extended.
     nsecs_t scheduled;
 
+    // True if this timer is split and in its second half
+    bool splitting;
+
     // True if this timer has been extended.
     bool extended;
 
@@ -367,22 +696,10 @@
 
     // The default constructor is used to create timers that are Invalid, representing the "not
     // found" condition when a collection is searched.
-    Timer() :
-            id(NOTIMER),
-            pid(0),
-            uid(0),
-            timeout(0),
-            extend(false),
-            freeze(false),
-            status(Invalid),
-            started(0),
-            scheduled(0),
-            extended(false),
-            frozen(false) {
-    }
+    Timer() : Timer(NOTIMER) { }
 
-    // This constructor creates a timer with the specified id.  This can be used as the argument
-    // to find().
+    // This constructor creates a timer with the specified id and everything else set to
+    // "empty".  This can be used as the argument to find().
     Timer(timer_id_t id) :
             id(id),
             pid(0),
@@ -390,29 +707,37 @@
             timeout(0),
             extend(false),
             freeze(false),
+            split(0),
+            action(AnrTimerTracer::None),
             status(Invalid),
             started(0),
             scheduled(0),
+            splitting(false),
             extended(false),
             frozen(false) {
     }
 
     // Create a new timer.  This starts the timer.
-    Timer(int pid, int uid, nsecs_t timeout, bool extend, bool freeze) :
+    Timer(int pid, int uid, nsecs_t timeout, bool extend, bool freeze,
+          AnrTimerTracer::TraceConfig trace) :
             id(nextId()),
             pid(pid),
             uid(uid),
             timeout(timeout),
             extend(extend),
             freeze(pid != 0 && freeze),
+            split(trace.earlyTimeout),
+            action(trace.action),
             status(Running),
             started(now()),
-            scheduled(started + timeout),
+            scheduled(started + (split > 0 ? (timeout*split)/100 : timeout)),
+            splitting(false),
             extended(false),
             frozen(false) {
         if (extend && pid != 0) {
             initial.fill(pid);
         }
+
         // A zero-pid is odd but it means the upper layers will never ANR the process.  Freezing
         // is always disabled.  (It won't work anyway, but disabling it avoids error messages.)
         ALOGI_IF(DEBUG_ERROR && pid == 0, "error: zero-pid %s", toString().c_str());
@@ -434,6 +759,23 @@
     // returns false if the timer is eligible for extension.  If the function returns false, the
     // scheduled time is updated.
     bool expire() {
+        if (split > 0 && !splitting) {
+            scheduled = started + timeout;
+            splitting = true;
+            event("split");
+            switch (action) {
+                case AnrTimerTracer::None:
+                case AnrTimerTracer::Trace:
+                    break;
+                case AnrTimerTracer::Expire:
+                    status = Expired;
+                    maybeFreezeProcess();
+                    event("expire");
+                    break;
+            }
+            return status == Expired;
+        }
+
         nsecs_t extension = 0;
         if (extend && !extended) {
             // Only one extension is permitted.
@@ -525,15 +867,15 @@
 
         char tag[PATH_MAX];
         snprintf(tag, sizeof(tag), "freeze(pid=%d,uid=%d)", pid, uid);
-        ATRACE_ASYNC_FOR_TRACK_BEGIN(ANR_TIMER_TRACK, tag, cookie);
+        traceBegin(tag, cookie);
         if (SetProcessProfiles(uid, pid, {"Frozen"})) {
             ALOGI("freeze %s name=%s", toString().c_str(), getName().c_str());
             frozen = true;
-            ATRACE_ASYNC_FOR_TRACK_BEGIN(ANR_TIMER_TRACK, "frozen", cookie+1);
+            traceBegin("frozen", cookie+1);
         } else {
             ALOGE("error: freezing %s name=%s error=%s",
                   toString().c_str(), getName().c_str(), strerror(errno));
-            ATRACE_ASYNC_FOR_TRACK_END(ANR_TIMER_TRACK, cookie);
+            traceEnd(cookie);
         }
     }
 
@@ -543,7 +885,7 @@
         // See maybeFreezeProcess for an explanation of the cookie.
         const uint32_t cookie = id << 1;
 
-        ATRACE_ASYNC_FOR_TRACK_END(ANR_TIMER_TRACK, cookie+1);
+        traceEnd(cookie+1);
         if (SetProcessProfiles(uid, pid, {"Unfrozen"})) {
             ALOGI("unfreeze %s name=%s", toString().c_str(), getName().c_str());
             frozen = false;
@@ -551,7 +893,7 @@
             ALOGE("error: unfreezing %s name=%s error=%s",
                   toString().c_str(), getName().c_str(), strerror(errno));
         }
-        ATRACE_ASYNC_FOR_TRACK_END(ANR_TIMER_TRACK, cookie);
+        traceEnd(cookie);
     }
 
     // Get the next free ID.  NOTIMER is never returned.
@@ -564,12 +906,17 @@
     }
 
     // Log an event, non-verbose.
-    void event(char const* tag) {
+    void event(const char* tag) {
         event(tag, false);
     }
 
     // Log an event, guarded by the debug flag.
-    void event(char const* tag, bool verbose) {
+    void event(const char* tag, bool verbose) {
+        if (action != AnrTimerTracer::None) {
+            char msg[PATH_MAX];
+            snprintf(msg, sizeof(msg), "%s(pid=%d)", tag, pid);
+            traceEvent(msg);
+        }
         if (verbose) {
             char name[PATH_MAX];
             ALOGI_IF(DEBUG_TIMER, "event %s %s name=%s",
@@ -594,12 +941,12 @@
     struct Entry {
         const nsecs_t scheduled;
         const timer_id_t id;
-        AnrTimerService* const service;
+        AnrTimerService* service;
 
         Entry(nsecs_t scheduled, timer_id_t id, AnrTimerService* service) :
                 scheduled(scheduled), id(id), service(service) {};
 
-        bool operator<(const Entry &r) const {
+        bool operator<(const Entry& r) const {
             return scheduled == r.scheduled ? id < r.id : scheduled < r.scheduled;
         }
     };
@@ -664,7 +1011,7 @@
     }
 
     // Remove every timer associated with the service.
-    void remove(AnrTimerService const* service) {
+    void remove(const AnrTimerService* service) {
         AutoMutex _l(lock_);
         timer_id_t front = headTimerId();
         for (auto i = running_.begin(); i != running_.end(); ) {
@@ -746,7 +1093,7 @@
     // scheduled expiration time of the first entry.
     void restartLocked() {
         if (!running_.empty()) {
-            Entry const x = *(running_.cbegin());
+            const Entry x = *(running_.cbegin());
             nsecs_t delay = x.scheduled - now();
             // Force a minimum timeout of 10ns.
             if (delay < 10) delay = 10;
@@ -807,7 +1154,7 @@
 std::atomic<size_t> AnrTimerService::Ticker::idGen_;
 
 
-AnrTimerService::AnrTimerService(char const* label, notifier_t notifier, void* cookie,
+AnrTimerService::AnrTimerService(const char* label, notifier_t notifier, void* cookie,
             jweak jtimer, Ticker* ticker, bool extend, bool freeze) :
         label_(label),
         notifier_(notifier),
@@ -841,7 +1188,7 @@
 
 AnrTimerService::timer_id_t AnrTimerService::start(int pid, int uid, nsecs_t timeout) {
     AutoMutex _l(lock_);
-    Timer t(pid, uid, timeout, extend_, freeze_);
+    Timer t(pid, uid, timeout, extend_, freeze_, tracer_.getConfig(pid));
     insertLocked(t);
     t.start();
     counters_.started++;
@@ -918,7 +1265,7 @@
     return okay;
 }
 
-void AnrTimerService::addExpiredLocked(Timer const& timer) {
+void AnrTimerService::addExpiredLocked(const Timer& timer) {
     scrubExpiredLocked();
     expired_.insert(timer);
 }
@@ -1077,7 +1424,7 @@
     ScopedUtfChars name(env, jname);
     jobject timer = env->NewWeakGlobalRef(jtimer);
     AnrTimerService* service = new AnrTimerService(name.c_str(),
-            anrNotify, &gAnrArgs, timer, gAnrArgs.ticker, extend, freeze);
+        anrNotify, &gAnrArgs, timer, gAnrArgs.ticker, extend, freeze);
     return reinterpret_cast<jlong>(service);
 }
 
@@ -1122,6 +1469,19 @@
     return toService(ptr)->release(timerId);
 }
 
+jstring anrTimerTrace(JNIEnv* env, jclass, jobjectArray jconfig) {
+    if (!nativeSupportEnabled) return nullptr;
+    std::vector<std::string> config;
+    const jsize jlen = jconfig == nullptr ? 0 : env->GetArrayLength(jconfig);
+    for (size_t i = 0; i < jlen; i++) {
+        jstring je = static_cast<jstring>(env->GetObjectArrayElement(jconfig, i));
+        ScopedUtfChars e(env, je);
+        config.push_back(e.c_str());
+    }
+    auto r = AnrTimerService::trace(config);
+    return env->NewStringUTF(r.second.c_str());
+}
+
 jobjectArray anrTimerDump(JNIEnv *env, jclass, jlong ptr) {
     if (!nativeSupportEnabled) return nullptr;
     std::vector<std::string> stats = toService(ptr)->getDump();
@@ -1134,22 +1494,23 @@
 }
 
 static const JNINativeMethod methods[] = {
-    {"nativeAnrTimerSupported", "()Z",  (void*) anrTimerSupported},
-    {"nativeAnrTimerCreate",   "(Ljava/lang/String;ZZ)J", (void*) anrTimerCreate},
-    {"nativeAnrTimerClose",    "(J)I",     (void*) anrTimerClose},
-    {"nativeAnrTimerStart",    "(JIIJ)I",  (void*) anrTimerStart},
-    {"nativeAnrTimerCancel",   "(JI)Z",    (void*) anrTimerCancel},
-    {"nativeAnrTimerAccept",   "(JI)Z",    (void*) anrTimerAccept},
-    {"nativeAnrTimerDiscard",  "(JI)Z",    (void*) anrTimerDiscard},
-    {"nativeAnrTimerRelease",  "(JI)Z",    (void*) anrTimerRelease},
-    {"nativeAnrTimerDump",     "(J)[Ljava/lang/String;", (void*) anrTimerDump},
+    {"nativeAnrTimerSupported",   "()Z",        (void*) anrTimerSupported},
+    {"nativeAnrTimerCreate",      "(Ljava/lang/String;ZZ)J", (void*) anrTimerCreate},
+    {"nativeAnrTimerClose",       "(J)I",       (void*) anrTimerClose},
+    {"nativeAnrTimerStart",       "(JIIJ)I",    (void*) anrTimerStart},
+    {"nativeAnrTimerCancel",      "(JI)Z",      (void*) anrTimerCancel},
+    {"nativeAnrTimerAccept",      "(JI)Z",      (void*) anrTimerAccept},
+    {"nativeAnrTimerDiscard",     "(JI)Z",      (void*) anrTimerDiscard},
+    {"nativeAnrTimerRelease",     "(JI)Z",      (void*) anrTimerRelease},
+    {"nativeAnrTimerTrace",       "([Ljava/lang/String;)Ljava/lang/String;", (void*) anrTimerTrace},
+    {"nativeAnrTimerDump",        "(J)[Ljava/lang/String;", (void*) anrTimerDump},
 };
 
 } // anonymous namespace
 
 int register_android_server_utils_AnrTimer(JNIEnv* env)
 {
-    static const char *className = "com/android/server/utils/AnrTimer";
+    static const char* className = "com/android/server/utils/AnrTimer";
     jniRegisterNativeMethods(env, className, methods, NELEM(methods));
 
     nativeSupportEnabled = NATIVE_SUPPORT;
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 0eafb59..a07facf 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -512,8 +512,6 @@
                 <xs:annotation name="final"/>
             </xs:element>
         </xs:sequence>
-        <!-- valid value of interpolation if specified: linear -->
-        <xs:attribute name="interpolation" type="xs:string" use="optional"/>
     </xs:complexType>
 
     <xs:complexType name="brightnessPoint">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 355b0ab..5309263 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -91,8 +91,6 @@
   public class ComprehensiveBrightnessMap {
     ctor public ComprehensiveBrightnessMap();
     method @NonNull public final java.util.List<com.android.server.display.config.BrightnessPoint> getBrightnessPoint();
-    method public String getInterpolation();
-    method public void setInterpolation(String);
   }
 
   public class Density {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6314b85..4e89b85 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3515,6 +3515,48 @@
         return true;
     }
 
+    @GuardedBy("getLockObject()")
+    private boolean maybeMigrateResetPasswordTokenLocked(String backupId) {
+        if (!Flags.resetPasswordWithTokenCoexistence()) {
+            Slog.i(LOG_TAG, "ResetPasswordWithToken not migrated because coexistence "
+                    + "support is not enabled.");
+            return false;
+        }
+        if (mOwners.isResetPasswordWithTokenMigrated()) {
+            // TODO(b/359187209): Remove log after Flags.resetPasswordWithTokenCoexistence full
+            //  rollout.
+            Slog.v(LOG_TAG, "ResetPasswordWithToken was previously migrated to "
+                    + "policy engine.");
+            return false;
+        }
+
+        Slog.i(LOG_TAG, "Migrating ResetPasswordWithToken to policy engine");
+
+        // Create backup if none exists
+        mDevicePolicyEngine.createBackup(backupId);
+        try {
+            iterateThroughDpcAdminsLocked((admin, enforcingAdmin) -> {
+                int userId = enforcingAdmin.getUserId();
+                DevicePolicyData policy = getUserData(userId);
+                if (policy.mPasswordTokenHandle != 0) {
+                    Slog.i(LOG_TAG, "Setting RESET_PASSWORD_TOKEN policy");
+                    mDevicePolicyEngine.setLocalPolicy(
+                            PolicyDefinition.RESET_PASSWORD_TOKEN,
+                            enforcingAdmin,
+                            new LongPolicyValue(policy.mPasswordTokenHandle),
+                            userId);
+                }
+            });
+        } catch (Exception e) {
+            Slog.wtf(LOG_TAG,
+                    "Failed to migrate ResetPasswordWithToken to policy engine", e);
+        }
+
+        Slog.i(LOG_TAG, "Marking ResetPasswordWithToken migration complete");
+        mOwners.markResetPasswordWithTokenMigrated();
+        return true;
+    }
+
     /** Register callbacks for statsd pulled atoms. */
     private void registerStatsCallbacks() {
         final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
@@ -4110,8 +4152,10 @@
     }
 
     private void checkAllUsersAreAffiliatedWithDevice() {
-        Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked(),
-                "operation not allowed when device has unaffiliated users");
+        synchronized (getLockObject()) {
+            Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked(),
+                    "operation not allowed when device has unaffiliated users");
+        }
     }
 
     @Override
@@ -6395,7 +6439,7 @@
     @Override
     public void lockNow(int flags, String callerPackageName, boolean parent) {
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.lockNowCoexistence()) {
             caller = getCallerIdentity(callerPackageName);
         } else {
             caller = getCallerIdentity();
@@ -6407,7 +6451,7 @@
             ActiveAdmin admin;
             // Make sure the caller has any active admin with the right policy or
             // the required permission.
-            if (isUnicornFlagEnabled()) {
+            if (Flags.lockNowCoexistence()) {
                 admin = enforcePermissionsAndGetEnforcingAdmin(
                         /* admin= */ null,
                         /* permissions= */ new String[]{MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE},
@@ -9179,13 +9223,13 @@
         }
 
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeEnabledCoexistence()) {
             caller = getCallerIdentity(who, callerPackageName);
         } else {
             caller = getCallerIdentity(who);
         }
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeEnabledCoexistence()) {
             // The effect of this policy is device-wide.
             enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
         } else {
@@ -9213,13 +9257,13 @@
             return false;
         }
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeEnabledCoexistence()) {
             caller = getCallerIdentity(who, callerPackageName);
         } else {
             caller = getCallerIdentity(who);
         }
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeEnabledCoexistence()) {
             enforceCanQuery(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
         } else {
             Objects.requireNonNull(who, "ComponentName is null");
@@ -9242,13 +9286,13 @@
         }
 
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
             caller = getCallerIdentity(who, callerPackageName);
         } else {
             caller = getCallerIdentity(who);
         }
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
             // The effect of this policy is device-wide.
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     who,
@@ -9288,13 +9332,13 @@
         }
 
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
             caller = getCallerIdentity(who, callerPackageName);
         } else {
             caller = getCallerIdentity(who);
         }
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setAutoTimeZoneEnabledCoexistence()) {
             // The effect of this policy is device-wide.
             enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
         } else {
@@ -9544,7 +9588,7 @@
         }
 
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setKeyguardDisabledFeaturesCoexistence()) {
             caller = getCallerIdentity(who, callerPackageName);
         } else {
             caller = getCallerIdentity(who);
@@ -9554,7 +9598,7 @@
         final int userHandle = caller.getUserId();
         int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
         synchronized (getLockObject()) {
-            if (isUnicornFlagEnabled()) {
+            if (Flags.setKeyguardDisabledFeaturesCoexistence()) {
                 // SUPPORT USES_POLICY_DISABLE_KEYGUARD_FEATURES
                 EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
                         who, MANAGE_DEVICE_POLICY_KEYGUARD, caller.getPackageName(),
@@ -9633,7 +9677,7 @@
 
         synchronized (getLockObject()) {
             if (who != null) {
-                if (isUnicornFlagEnabled()) {
+                if (Flags.setKeyguardDisabledFeaturesCoexistence()) {
                     EnforcingAdmin admin = getEnforcingAdminForPackage(
                             who, who.getPackageName(), userHandle);
                     Integer features = mDevicePolicyEngine.getLocalPolicySetByAdmin(
@@ -9652,7 +9696,7 @@
             // the different behaviour between a profile with separate challenge vs a profile with
             // unified challenge, which was part of getActiveAdminsForLockscreenPoliciesLocked()
             // before the migration.
-            if (isUnicornFlagEnabled()) {
+            if (Flags.setKeyguardDisabledFeaturesCoexistence()) {
                 Integer features = mDevicePolicyEngine.getResolvedPolicy(
                         PolicyDefinition.KEYGUARD_DISABLED_FEATURES,
                         affectedUserId);
@@ -11320,7 +11364,7 @@
         if (mOwners.hasDeviceOwner()) {
             return false;
         }
-        
+
         final ComponentName profileOwner = getProfileOwnerAsUser(userId);
         if (profileOwner == null) {
             return false;
@@ -11329,7 +11373,7 @@
         if (isManagedProfile(userId)) {
             return false;
         }
-        
+
         return true;
     }
     private void enforceCanQueryLockTaskLocked(ComponentName who, String callerPackageName) {
@@ -11845,7 +11889,7 @@
             throw new IllegalArgumentException("Invalid package name: " + validationResult);
         }
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setApplicationRestrictionsCoexistence()) {
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     who,
                     MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
@@ -13228,7 +13272,10 @@
             String packageName, boolean parent) {
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
 
-        if (isUnicornFlagEnabled()) {
+        // IMPORTANT: The code behind the if branch is OUTDATED and requires additional work before
+        // enabling the feature flag below.
+        // TODO(b/369141952): Update DPM.getApplicationRestrictions coexistence code
+        if (Flags.setApplicationRestrictionsCoexistence()) {
             EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
                     who,
                     MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
@@ -13328,21 +13375,12 @@
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
         ActiveAdmin admin;
 
-        if (isUnicornFlagEnabled()) {
-            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                    who,
-                    MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-                    caller.getPackageName(),
-                    caller.getUserId());
-            admin = enforcingAdmin.getActiveAdmin();
-        } else {
-            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
-                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
-                    || (caller.hasPackage() && isCallerDelegate(caller,
-                    DELEGATION_PACKAGE_ACCESS)));
-            synchronized (getLockObject()) {
-                admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
-            }
+        Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+                && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+                || (caller.hasPackage() && isCallerDelegate(caller,
+                DELEGATION_PACKAGE_ACCESS)));
+        synchronized (getLockObject()) {
+            admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
         }
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PACKAGES_SUSPENDED);
 
@@ -15585,12 +15623,12 @@
     public boolean setStatusBarDisabled(ComponentName who, String callerPackageName,
             boolean disabled) {
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (isSetStatusBarDisabledCoexistenceEnabled()) {
             caller = getCallerIdentity(who, callerPackageName);
         } else {
             caller = getCallerIdentity(who);
         }
-        if (isUnicornFlagEnabled()) {
+        if (isSetStatusBarDisabledCoexistenceEnabled()) {
             enforcePermission(MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(),
                     UserHandle.USER_ALL);
         } else {
@@ -15601,7 +15639,7 @@
 
         int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            if (!isUnicornFlagEnabled()) {
+            if (!isSetStatusBarDisabledCoexistenceEnabled()) {
                 Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
                         "Admin " + who + " is neither the device owner or affiliated "
                                 + "user's profile owner.");
@@ -15660,7 +15698,7 @@
     @Override
     public boolean isStatusBarDisabled(String callerPackage) {
         final CallerIdentity caller = getCallerIdentity(callerPackage);
-        if (isUnicornFlagEnabled()) {
+        if (isSetStatusBarDisabledCoexistenceEnabled()) {
             enforceCanQuery(
                     MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), caller.getUserId());
         } else {
@@ -15670,7 +15708,7 @@
 
         int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            if (!isUnicornFlagEnabled()) {
+            if (!isSetStatusBarDisabledCoexistenceEnabled()) {
                 Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
                         "Admin " + callerPackage
                                 + " is neither the device owner or affiliated user's profile owner.");
@@ -16862,7 +16900,7 @@
             }
         }
         EnforcingAdmin enforcingAdmin;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setPermissionGrantStateCoexistence()) {
             enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     admin,
                     MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
@@ -17047,7 +17085,7 @@
     public int getPermissionGrantState(ComponentName admin, String callerPackage,
             String packageName, String permission) throws RemoteException {
         final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
-        if (isUnicornFlagEnabled()) {
+        if (Flags.setPermissionGrantStateCoexistence()) {
             enforceCanQuery(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, caller.getPackageName(),
                     caller.getUserId());
         } else {
@@ -18177,6 +18215,7 @@
         return false;
     }
 
+    @GuardedBy("getLockObject()")
     private boolean areAllUsersAffiliatedWithDeviceLocked() {
         return mInjector.binderWithCleanCallingIdentity(() -> {
             final List<UserInfo> userInfos = mUserManager.getAliveUsers();
@@ -18274,10 +18313,12 @@
 
         final CallerIdentity caller = getCallerIdentity(admin, packageName);
         if (isPermissionCheckFlagEnabled()) {
-            Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
-                    || areAllUsersAffiliatedWithDeviceLocked());
-            enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
-                    UserHandle.USER_ALL);
+            synchronized (getLockObject()) {
+                Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+                        || areAllUsersAffiliatedWithDeviceLocked());
+                enforcePermission(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, caller.getPackageName(),
+                        UserHandle.USER_ALL);
+            }
         } else {
             if (admin != null) {
                 Preconditions.checkCallAuthorization(
@@ -18289,8 +18330,10 @@
                         isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
             }
 
-            Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
-                    || areAllUsersAffiliatedWithDeviceLocked());
+            synchronized (getLockObject()) {
+                Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
+                        || areAllUsersAffiliatedWithDeviceLocked());
+            }
         }
 
         DevicePolicyEventLogger
@@ -19331,14 +19374,14 @@
             throw new IllegalArgumentException("token must be at least 32-byte long");
         }
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             caller = getCallerIdentity(admin, callerPackageName);
         } else {
             caller = getCallerIdentity(admin);
         }
         final int userId = caller.getUserId();
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     admin,
                     MANAGE_DEVICE_POLICY_RESET_PASSWORD,
@@ -19351,6 +19394,10 @@
             long tokenHandle = addEscrowToken(
                     token, currentTokenHandle == null ? 0 : currentTokenHandle, userId);
             if (tokenHandle == 0) {
+                mDevicePolicyEngine.removeLocalPolicy(
+                        PolicyDefinition.RESET_PASSWORD_TOKEN,
+                        enforcingAdmin,
+                        userId);
                 return false;
             }
             mDevicePolicyEngine.setLocalPolicy(
@@ -19394,7 +19441,7 @@
             return false;
         }
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             caller = getCallerIdentity(admin, callerPackageName);
         } else {
             caller = getCallerIdentity(admin);
@@ -19402,7 +19449,7 @@
         final int userId = caller.getUserId();
         boolean result = false;
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     admin,
                     MANAGE_DEVICE_POLICY_RESET_PASSWORD,
@@ -19441,14 +19488,14 @@
             return false;
         }
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             caller = getCallerIdentity(admin, callerPackageName);
         } else {
             caller = getCallerIdentity(admin);
         }
         int userId = caller.getUserId();
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     admin,
                     MANAGE_DEVICE_POLICY_RESET_PASSWORD,
@@ -19490,7 +19537,7 @@
         Objects.requireNonNull(token);
 
         CallerIdentity caller;
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             caller = getCallerIdentity(admin, callerPackageName);
         } else {
             caller = getCallerIdentity(admin);
@@ -19500,7 +19547,7 @@
         boolean result = false;
         final String password = passwordOrNull != null ? passwordOrNull : "";
 
-        if (isUnicornFlagEnabled()) {
+        if (Flags.resetPasswordWithTokenCoexistence()) {
             EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                     admin,
                     MANAGE_DEVICE_POLICY_RESET_PASSWORD,
@@ -19531,7 +19578,7 @@
         }
 
         if (result) {
-            if (isUnicornFlagEnabled()) {
+            if (Flags.resetPasswordWithTokenCoexistence()) {
                 DevicePolicyEventLogger
                         .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
                         .setAdmin(callerPackageName)
@@ -23812,7 +23859,7 @@
                 DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
     }
 
-    static boolean isUnicornFlagEnabled() {
+    private static boolean isSetStatusBarDisabledCoexistenceEnabled() {
         return false;
     }
 
@@ -24255,8 +24302,11 @@
                 synchronized (getLockObject()) {
                     Slogf.i(LOG_TAG,
                             "Started device policies migration to the device policy engine.");
-                    if (isUnicornFlagEnabled()) {
+                    // TODO(b/359188869): Move this to the current migration method.
+                    if (Flags.setAutoTimeZoneEnabledCoexistence()) {
                         migrateAutoTimezonePolicy();
+                    }
+                    if (Flags.setPermissionGrantStateCoexistence()) {
                         migratePermissionGrantStatePolicies();
                     }
                     migratePermittedInputMethodsPolicyLocked();
@@ -24283,12 +24333,21 @@
         maybeMigrateSecurityLoggingPolicyLocked();
         // ID format: <sdk-int>.<auto_increment_id>.<descriptions>'
         String unmanagedBackupId = "35.1.unmanaged-mode";
-        boolean migrated = false;
-        migrated = migrated | maybeMigrateRequiredPasswordComplexityLocked(unmanagedBackupId);
-        migrated = migrated | maybeMigrateSuspendedPackagesLocked(unmanagedBackupId);
-        if (migrated) {
+        boolean unmanagedMigrated = false;
+        unmanagedMigrated =
+                unmanagedMigrated | maybeMigrateRequiredPasswordComplexityLocked(unmanagedBackupId);
+        unmanagedMigrated =
+                unmanagedMigrated | maybeMigrateSuspendedPackagesLocked(unmanagedBackupId);
+        if (unmanagedMigrated) {
             Slogf.i(LOG_TAG, "Backup made: " + unmanagedBackupId);
         }
+
+        String supervisionBackupId = "36.2.supervision-support";
+        boolean supervisionMigrated = maybeMigrateResetPasswordTokenLocked(supervisionBackupId);
+        if (supervisionMigrated) {
+            Slogf.i(LOG_TAG, "Backup made: " + supervisionBackupId);
+        }
+
         // Additional migration steps should repeat the pattern above with a new backupId.
     }
 
@@ -24488,7 +24547,8 @@
             }
         });
     }
-    
+
+    @GuardedBy("getLockObject()")
     private void migrateUserControlDisabledPackagesLocked() {
         Binder.withCleanCallingIdentity(() -> {
             List<UserInfo> users = mUserManager.getUsers();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 3f9605a..b3c8408 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -669,6 +669,19 @@
         }
     }
 
+    void markResetPasswordWithTokenMigrated() {
+        synchronized (mData) {
+            mData.mResetPasswordWithTokenMigrated = true;
+            mData.writeDeviceOwner();
+        }
+    }
+
+    boolean isResetPasswordWithTokenMigrated() {
+        synchronized (mData) {
+            return mData.mResetPasswordWithTokenMigrated;
+        }
+    }
+
     @GuardedBy("mData")
     void pushToAppOpsLocked() {
         if (!mSystemReady) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 87fd002..10e43d9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -91,6 +91,8 @@
     private static final String ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED =
             "passwordComplexityMigrated";
     private static final String ATTR_SUSPENDED_PACKAGES_MIGRATED = "suspendedPackagesMigrated";
+    private static final String ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED =
+            "resetPasswordWithTokenMigrated";
     private static final String ATTR_MIGRATED_POST_UPGRADE = "migratedPostUpgrade";
 
     // Internal state for the device owner package.
@@ -122,6 +124,7 @@
     boolean mSecurityLoggingMigrated = false;
     boolean mRequiredPasswordComplexityMigrated = false;
     boolean mSuspendedPackagesMigrated = false;
+    boolean mResetPasswordWithTokenMigrated = false;
 
     boolean mPoliciesMigratedPostUpdate = false;
 
@@ -417,7 +420,10 @@
                         mSuspendedPackagesMigrated);
 
             }
-
+            if (Flags.resetPasswordWithTokenCoexistence()) {
+                out.attributeBoolean(null, ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED,
+                        mResetPasswordWithTokenMigrated);
+            }
             out.endTag(null, TAG_POLICY_ENGINE_MIGRATION);
 
         }
@@ -488,6 +494,9 @@
                     mSuspendedPackagesMigrated = Flags.unmanagedModeMigration()
                             && parser.getAttributeBoolean(null,
                                     ATTR_SUSPENDED_PACKAGES_MIGRATED, false);
+                    mResetPasswordWithTokenMigrated = Flags.resetPasswordWithTokenCoexistence()
+                            && parser.getAttributeBoolean(null,
+                            ATTR_RESET_PASSWORD_WITH_TOKEN_MIGRATED, false);
 
                     break;
                 default:
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 8068d46..4d9abf1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -78,7 +78,7 @@
     }
 
     static boolean setAutoTimezoneEnabled(@Nullable Boolean enabled, @NonNull Context context) {
-        if (!DevicePolicyManagerService.isUnicornFlagEnabled()) {
+        if (!Flags.setAutoTimeZoneEnabledCoexistence()) {
             Slogf.w(LOG_TAG, "Trying to enforce setAutoTimezoneEnabled while flag is off.");
             return true;
         }
@@ -95,7 +95,7 @@
     static boolean setPermissionGrantState(
             @Nullable Integer grantState, @NonNull Context context, int userId,
             @NonNull PolicyKey policyKey) {
-        if (!DevicePolicyManagerService.isUnicornFlagEnabled()) {
+        if (!Flags.setPermissionGrantStateCoexistence()) {
             Slogf.w(LOG_TAG, "Trying to enforce setPermissionGrantState while flag is off.");
             return true;
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9fdf088..ce6f1ec 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.ApplicationSharedMemory;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.policy.AttributeCache;
@@ -204,6 +205,7 @@
 import com.android.server.pdb.PersistentDataBlockService;
 import com.android.server.people.PeopleService;
 import com.android.server.permission.access.AccessCheckingService;
+import com.android.server.pinner.PinnerService;
 import com.android.server.pm.ApexManager;
 import com.android.server.pm.ApexSystemServiceInfo;
 import com.android.server.pm.BackgroundInstallControlService;
@@ -436,6 +438,10 @@
     private static final String PROFILING_SERVICE_JAR_PATH =
             "/apex/com.android.profiling/javalib/service-profiling.jar";
 
+    private static final String RANGING_APEX_SERVICE_JAR_PATH =
+            "/apex/com.android.uwb/javalib/service-ranging.jar";
+    private static final String RANGING_SERVICE_CLASS = "com.android.server.ranging.RangingService";
+
     private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -936,6 +942,12 @@
         // Setup the default WTF handler
         RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf);
 
+        // Initialize the application shared memory region.
+        // This needs to happen before any system services are started,
+        // as they may rely on the shared memory region having been initialized.
+        ApplicationSharedMemory instance = ApplicationSharedMemory.create();
+        ApplicationSharedMemory.setInstance(instance);
+
         // Start services.
         try {
             t.traceBegin("StartServices");
@@ -3005,9 +3017,13 @@
         }
         t.traceEnd();
 
-        t.traceBegin("GameManagerService");
-        mSystemServiceManager.startService(GameManagerService.Lifecycle.class);
-        t.traceEnd();
+        if (!isWatch || !android.server.Flags.removeGameManagerServiceFromWear()) {
+            t.traceBegin("GameManagerService");
+            mSystemServiceManager.startService(GameManagerService.Lifecycle.class);
+            t.traceEnd();
+        } else {
+            Slog.d(TAG, "Not starting GameManagerService");
+        }
 
         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
             t.traceBegin("UwbService");
@@ -3015,6 +3031,17 @@
             t.traceEnd();
         }
 
+        if (com.android.ranging.flags.Flags.rangingStackEnabled()) {
+            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
+                    || context.getPackageManager().hasSystemFeature(
+                            PackageManager.FEATURE_WIFI_RTT)) {
+                t.traceBegin("RangingService");
+                mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS,
+                        RANGING_APEX_SERVICE_JAR_PATH);
+                t.traceEnd();
+            }
+        }
+
         t.traceBegin("StartBootPhaseDeviceSpecificServicesReady");
         mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
         t.traceEnd();
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index ec74ef19..0990691 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -35,4 +35,12 @@
     namespace: "wear_systems"
     description: "Allow NetworkTimeUpdateService on Wear"
     bug: "327508176"
+}
+
+flag {
+     name: "remove_game_manager_service_from_wear"
+     namespace: "wear_frameworks"
+     description: "Remove GameManagerService from Wear"
+     bug: "340929737"
+     is_fixed_read_only: true
 }
\ No newline at end of file
diff --git a/services/supervision/Android.bp b/services/supervision/Android.bp
index 93a0c4a..aefbbca 100644
--- a/services/supervision/Android.bp
+++ b/services/supervision/Android.bp
@@ -19,4 +19,7 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.supervision-sources"],
     libs: ["services.core"],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java b/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java
new file mode 100644
index 0000000..fead05b
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.supervision;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.Bundle;
+
+/**
+ * Local system service interface for {@link SupervisionService}.
+ *
+ * @hide Only for use within Android OS.
+ */
+public abstract class SupervisionManagerInternal {
+    /**
+     * Returns whether supervision is enabled for the specified user
+     *
+     * @param userId The user to retrieve the supervision state for
+     * @return whether the user is supervised
+     */
+    public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId);
+
+    /**
+     * Sets whether the supervision lock screen should be shown for the specified user
+     *
+     * @param userId The user set the superivision state for
+     * @param enabled Whether or not the superivision lock screen needs to be shown
+     * @param options Optional configuration parameters for the supervision lock screen
+     */
+    public abstract void setSupervisionLockscreenEnabledForUser(
+            @UserIdInt int userId, boolean enabled, @Nullable Bundle options);
+}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 7ffd0ec..4c515c1 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -18,14 +18,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.supervision.ISupervisionManager;
 import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -38,13 +46,25 @@
 
     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 UserManagerInternal mUserManagerInternal;
+
     public SupervisionService(Context context) {
-        mContext = context.createAttributionContext("SupervisionService");
+        mContext = context.createAttributionContext(LOG_TAG);
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+        mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
     }
 
     @Override
-    public boolean isSupervisionEnabled() {
-        return false;
+    public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
+        synchronized (getLockObject()) {
+            return getUserDataLocked(userId).supervisionEnabled;
+        }
     }
 
     @Override
@@ -60,11 +80,44 @@
     }
 
     @Override
-    protected void dump(
-            @NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
+    protected void dump(@NonNull FileDescriptor fd,
+            @NonNull PrintWriter printWriter, @Nullable String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, printWriter)) return;
 
-        fout.println("Supervision enabled: " + isSupervisionEnabled());
+        try (var pw = new IndentingPrintWriter(printWriter, "  ")) {
+            pw.println("SupervisionService state:");
+            pw.increaseIndent();
+
+            var users = mUserManagerInternal.getUsers(false);
+            synchronized (getLockObject()) {
+                for (var user : users) {
+                    getUserDataLocked(user.id).dump(pw);
+                    pw.println();
+                }
+            }
+        }
+    }
+
+    private Object getLockObject() {
+        return mLockDoNoUseDirectly;
+    }
+
+    @NonNull
+    @GuardedBy("getLockObject()")
+    SupervisionUserData getUserDataLocked(@UserIdInt int userId) {
+        SupervisionUserData data = mUserData.get(userId);
+        if (data == null) {
+            // TODO(b/362790738): Do not create user data for nonexistent users.
+            data = new SupervisionUserData(userId);
+            mUserData.append(userId, data);
+        }
+        return data;
+    }
+
+    void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+        synchronized (getLockObject()) {
+            getUserDataLocked(userId).supervisionEnabled = enabled;
+        }
     }
 
     public static class Lifecycle extends SystemService {
@@ -77,7 +130,35 @@
 
         @Override
         public void onStart() {
+            publishLocalService(SupervisionManagerInternal.class, mSupervisionService.mInternal);
             publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService);
         }
     }
+
+    final SupervisionManagerInternal mInternal = new SupervisionManagerInternal() {
+        public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
+            synchronized (getLockObject()) {
+                return getUserDataLocked(userId).supervisionEnabled;
+            }
+        }
+
+        @Override
+        public void setSupervisionLockscreenEnabledForUser(
+                @UserIdInt int userId, boolean enabled, @Nullable Bundle options) {
+            synchronized (getLockObject()) {
+                SupervisionUserData data = getUserDataLocked(userId);
+                data.supervisionLockScreenEnabled = enabled;
+                data.supervisionLockScreenOptions = options;
+            }
+        }
+    };
+
+    private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener {
+        @Override
+        public void onUserRemoved(UserInfo user) {
+            synchronized (getLockObject()) {
+                mUserData.remove(user.id);
+            }
+        }
+    }
 }
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
index 3aba24a..2adaae3 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -17,8 +17,7 @@
 package com.android.server.supervision;
 
 import android.os.ShellCommand;
-
-import java.io.PrintWriter;
+import android.os.UserHandle;
 
 public class SupervisionServiceShellCommand extends ShellCommand {
     private final SupervisionService mService;
@@ -32,30 +31,29 @@
         if (cmd == null) {
             return handleDefaultCommands(null);
         }
-        final PrintWriter pw = getOutPrintWriter();
         switch (cmd) {
-            case "help": return help(pw);
-            case "is-enabled": return isEnabled(pw);
+            case "enable": return setEnabled(true);
+            case "disable": return setEnabled(false);
             default: return handleDefaultCommands(cmd);
         }
     }
 
-    private int help(PrintWriter pw) {
-        pw.println("Supervision service commands:");
-        pw.println("  help");
-        pw.println("      Prints this help text");
-        pw.println("  is-enabled");
-        pw.println("      Is supervision enabled");
-        return 0;
-    }
-
-    private int isEnabled(PrintWriter pw) {
-        pw.println(mService.isSupervisionEnabled());
+    private int setEnabled(boolean enabled) {
+        final var pw = getOutPrintWriter();
+        final var userId = UserHandle.parseUserArg(getNextArgRequired());
+        mService.setSupervisionEnabledForUser(userId, enabled);
         return 0;
     }
 
     @Override
     public void onHelp() {
-        help(getOutPrintWriter());
+        final var pw = getOutPrintWriter();
+        pw.println("Supervision service (supervision) commands:");
+        pw.println("  help");
+        pw.println("      Prints this help text");
+        pw.println("  enable <USER_ID>");
+        pw.println("      Enables supervision for the given user.");
+        pw.println("  disable <USER_ID>");
+        pw.println("      Disables supervision for the given user.");
     }
 }
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
new file mode 100644
index 0000000..5616237
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.supervision;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.Bundle;
+import android.util.IndentingPrintWriter;
+
+/** User specific data, used internally by the {@link SupervisionService}. */
+public class SupervisionUserData {
+    public final @UserIdInt int userId;
+    public boolean supervisionEnabled;
+    public boolean supervisionLockScreenEnabled;
+    @Nullable public Bundle supervisionLockScreenOptions;
+
+    public SupervisionUserData(@UserIdInt int userId) {
+        this.userId = userId;
+    }
+
+    void dump(@NonNull IndentingPrintWriter pw) {
+        pw.println();
+        pw.println("User " + userId + ":");
+        pw.increaseIndent();
+        pw.println("supervisionEnabled: " + supervisionEnabled);
+        pw.println("supervisionLockScreenEnabled: " + supervisionLockScreenEnabled);
+        pw.println("supervisionLockScreenOptions: " + supervisionLockScreenOptions);
+        pw.decreaseIndent();
+    }
+}
diff --git a/services/supervision/lint-baseline.xml b/services/supervision/lint-baseline.xml
new file mode 100644
index 0000000..f2a5010
--- /dev/null
+++ b/services/supervision/lint-baseline.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha08" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha08">
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="isSupervisionEnabled should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="    @Override"
+        errorLine2="    ^">
+        <location
+            file="frameworks/base/services/supervision/java/com/android/server/supervision/SupervisionService.java"
+            line="45"
+            column="5"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="onShellCommand should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="    @Override"
+        errorLine2="    ^">
+        <location
+            file="frameworks/base/services/supervision/java/com/android/server/supervision/SupervisionService.java"
+            line="50"
+            column="5"/>
+    </issue>
+
+    <issue
+        id="MissingPermissionAnnotation"
+        message="dump should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+        errorLine1="    @Override"
+        errorLine2="    ^">
+        <location
+            file="frameworks/base/services/supervision/java/com/android/server/supervision/SupervisionService.java"
+            line="62"
+            column="5"/>
+    </issue>
+
+    <issue
+        id="SimpleRequiresNoPermission"
+        message="Method isSupervisionEnabled doesn&apos;t perform any permission checks, meaning it should be annotated with @RequiresNoPermission."
+        errorLine1="    @Override"
+        errorLine2="    ^">
+        <location
+            file="frameworks/base/services/supervision/java/com/android/server/supervision/SupervisionService.java"
+            line="45"
+            column="5"/>
+    </issue>
+
+</issues>
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
index e5d3153..72cbac3 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
@@ -35,6 +35,7 @@
 public final class InputMethodManagerServiceTests {
     static final int SYSTEM_DECORATION_SUPPORT_DISPLAY_ID = 2;
     static final int NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID = 3;
+    private static final int TEST_IME_USER_ID = 1;
 
     static InputMethodManagerService.ImeDisplayValidator sChecker =
             (displayId) -> {
@@ -102,7 +103,8 @@
                 null,
                 null,
                 null,
-                null));
+                null,
+                TEST_IME_USER_ID));
 
         history.dump(new PrintWriter(writer), "" /* prefix */);
 
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 5c4716d..7d5532f 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -57,6 +57,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.doReturn
@@ -383,6 +384,10 @@
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
                 PackageManager.PERMISSION_GRANTED
             }
+            whenever(this.checkPermission(
+                eq(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt())) {
+                PackageManager.PERMISSION_GRANTED
+            }
         }
         val mockSharedLibrariesImpl: SharedLibrariesImpl = mock {
             whenever(this.snapshot()) { this@mock }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
index b21c349..2144785 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
@@ -94,6 +94,7 @@
             ParsedService::getIntents,
             ParsedService::getProperties,
             Intent::getCategories,
+            Intent::getExtraIntentKeys,
             PackageUserState::getDisabledComponents,
             PackageUserState::getEnabledComponents,
             PackageUserState::getSharedLibraryOverlayPaths,
diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp
index 19c9136..3a73c39 100644
--- a/services/tests/RemoteProvisioningServiceTests/Android.bp
+++ b/services/tests/RemoteProvisioningServiceTests/Android.bp
@@ -31,7 +31,6 @@
         "service-rkp.impl",
         "services.core",
         "truth",
-        "truth-java8-extension",
     ],
     test_suites: [
         "device-tests",
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
index 007c0db..a1616c6 100644
--- a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
@@ -17,7 +17,6 @@
 package com.android.server.security.rkp;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth8.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
diff --git a/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionRuntimeMetadataTest.kt b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionRuntimeMetadataTest.kt
index dbbb2fe..da3e94f 100644
--- a/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionRuntimeMetadataTest.kt
+++ b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionRuntimeMetadataTest.kt
@@ -112,4 +112,28 @@
         assertThat(runtimeMetadata.appFunctionStaticMetadataQualifiedId)
             .isEqualTo("android\$apps-db/app_functions#com.pkg/funcId")
     }
+
+    @Test
+    fun setEnabled_true() {
+        val runtimeMetadata =
+            AppFunctionRuntimeMetadata.Builder("com.pkg", "funcId").setEnabled(true).build()
+
+        assertThat(runtimeMetadata.enabled).isTrue()
+    }
+
+    @Test
+    fun setEnabled_false() {
+        val runtimeMetadata =
+            AppFunctionRuntimeMetadata.Builder("com.pkg", "funcId").setEnabled(false).build()
+
+        assertThat(runtimeMetadata.enabled).isFalse()
+    }
+
+    @Test
+    fun setEnabled_null() {
+        val runtimeMetadata =
+            AppFunctionRuntimeMetadata.Builder("com.pkg", "funcId").setEnabled(null).build()
+
+        assertThat(runtimeMetadata.enabled).isNull()
+    }
 }
diff --git a/services/tests/appfunctions/src/android/app/appfunctions/GenericDocumentWrapperTest.kt b/services/tests/appfunctions/src/android/app/appfunctions/GenericDocumentWrapperTest.kt
new file mode 100644
index 0000000..413eb31
--- /dev/null
+++ b/services/tests/appfunctions/src/android/app/appfunctions/GenericDocumentWrapperTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.app.appfunctions
+
+import android.app.appsearch.GenericDocument
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+
+@RunWith(JUnit4::class)
+class GenericDocumentWrapperTest {
+
+    @Test
+    fun parcelUnparcel() {
+        val doc =
+            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                .setPropertyLong("test", 42)
+                .build()
+        val wrapper = GenericDocumentWrapper(doc)
+
+        val recovered = parcelUnparcel(wrapper)
+
+        assertThat(recovered.value.getPropertyLong("test")).isEqualTo(42)
+    }
+
+    @Test
+    fun parcelUnparcel_afterGetValue() {
+        val doc =
+            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                .setPropertyLong("test", 42)
+                .build()
+        val wrapper = GenericDocumentWrapper(doc)
+        assertThat(wrapper.value.getPropertyLong("test")).isEqualTo(42)
+
+        val recovered = parcelUnparcel(wrapper)
+
+        assertThat(recovered.value.getPropertyLong("test")).isEqualTo(42)
+    }
+
+
+    @Test
+    fun getValue() {
+        val doc =
+            GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                .setPropertyLong("test", 42)
+                .build()
+        val wrapper = GenericDocumentWrapper(doc)
+
+        assertThat(wrapper.value.getPropertyLong("test")).isEqualTo(42)
+    }
+
+    private fun parcelUnparcel(obj: GenericDocumentWrapper): GenericDocumentWrapper {
+        val parcel = Parcel.obtain()
+        try {
+            obj.writeToParcel(parcel, 0)
+            parcel.setDataPosition(0)
+            return GenericDocumentWrapper.CREATOR.createFromParcel(parcel)
+        } finally {
+            parcel.recycle()
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
index c05c381..bc64e15 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/MetadataSyncAdapterTest.kt
@@ -36,7 +36,6 @@
 import com.android.internal.infra.AndroidFuture
 import com.android.server.appfunctions.FutureAppSearchSession.FutureSearchResults
 import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors
 import java.util.concurrent.atomic.AtomicBoolean
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -46,7 +45,6 @@
 class MetadataSyncAdapterTest {
     private val context = InstrumentationRegistry.getInstrumentation().targetContext
     private val appSearchManager = context.getSystemService(AppSearchManager::class.java)
-    private val testExecutor = MoreExecutors.directExecutor()
     private val packageManager = context.packageManager
 
     @Test
@@ -138,8 +136,7 @@
             PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
         runtimeSearchSession.put(putDocumentsRequest).get()
         staticSearchSession.put(putDocumentsRequest).get()
-        val metadataSyncAdapter =
-            MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
+        val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)
 
         val submitSyncRequest =
             metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(
@@ -180,8 +177,7 @@
         val putDocumentsRequest: PutDocumentsRequest =
             PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
         staticSearchSession.put(putDocumentsRequest).get()
-        val metadataSyncAdapter =
-            MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
+        val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)
 
         val submitSyncRequest =
             metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(
@@ -236,8 +232,7 @@
         val putDocumentsRequest: PutDocumentsRequest =
             PutDocumentsRequest.Builder().addGenericDocuments(functionRuntimeMetadata).build()
         runtimeSearchSession.put(putDocumentsRequest).get()
-        val metadataSyncAdapter =
-            MetadataSyncAdapter(testExecutor, packageManager, appSearchManager)
+        val metadataSyncAdapter = MetadataSyncAdapter(packageManager, appSearchManager)
 
         val submitSyncRequest =
             metadataSyncAdapter.trySyncAppFunctionMetadataBlocking(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index fd05b26..8e1be9a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -392,7 +392,7 @@
     public void testInvalidLuxThrottling() throws Exception {
         setupDisplayDeviceConfigFromDisplayConfigFile(
                 getContent(getInvalidLuxThrottling(), getValidProxSensor(),
-                        /* includeIdleMode= */ true, /* enableEvenDimmer */ false));
+                        /* includeIdleMode= */ true, /* enableEvenDimmer= */ false));
 
         Map<DisplayDeviceConfig.BrightnessLimitMapType, Map<Float, Float>> luxThrottlingData =
                 mDisplayDeviceConfig.getLuxThrottlingData();
@@ -600,7 +600,7 @@
     public void testProximitySensorWithEmptyValuesFromDisplayConfig() throws IOException {
         setupDisplayDeviceConfigFromDisplayConfigFile(
                 getContent(getValidLuxThrottling(), getProxSensorWithEmptyValues(),
-                        /* includeIdleMode= */ true, /* enableEvenDimmer */ false));
+                        /* includeIdleMode= */ true, /* enableEvenDimmer= */ false));
         assertNull(mDisplayDeviceConfig.getProximitySensor());
     }
 
@@ -608,7 +608,7 @@
     public void testProximitySensorWithRefreshRatesFromDisplayConfig() throws IOException {
         setupDisplayDeviceConfigFromDisplayConfigFile(
                 getContent(getValidLuxThrottling(), getValidProxSensorWithRefreshRateAndVsyncRate(),
-                        /* includeIdleMode= */ true, /* enableEvenDimmer */ false));
+                        /* includeIdleMode= */ true, /* enableEvenDimmer= */ false));
         assertEquals("test_proximity_sensor",
                 mDisplayDeviceConfig.getProximitySensor().type);
         assertEquals("Test Proximity Sensor",
@@ -803,7 +803,7 @@
     @Test
     public void testBrightnessRamps_IdleFallsBackToConfigInteractive() throws IOException {
         setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
-                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
+                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer= */ false));
 
         assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
         assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(), 2000);
@@ -820,14 +820,14 @@
     @Test
     public void testBrightnessCapForWearBedtimeMode() throws IOException {
         setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
-                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
+                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer= */ false));
         assertEquals(0.1f, mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
     }
 
     @Test
     public void testAutoBrightnessBrighteningLevels() throws IOException {
         setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
-                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
+                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer= */ false));
 
         assertArrayEquals(new float[]{0.0f, 80},
                 mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
@@ -890,7 +890,7 @@
         when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(false);
         setupDisplayDeviceConfigFromConfigResourceFile();
         setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
-                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ false));
+                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer= */ false));
 
         assertArrayEquals(new float[]{brightnessIntToFloat(50), brightnessIntToFloat(100),
                         brightnessIntToFloat(150)},
@@ -929,7 +929,7 @@
         when(mFlags.isEvenDimmerEnabled()).thenReturn(true);
         when(mResources.getBoolean(R.bool.config_evenDimmerEnabled)).thenReturn(true);
         setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
-                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ true));
+                getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer= */ true));
 
         assertTrue(mDisplayDeviceConfig.isEvenDimmerAvailable());
         assertEquals(0.01f, mDisplayDeviceConfig.getBacklightFromBrightness(0.002f), ZERO_DELTA);
@@ -1365,7 +1365,7 @@
 
     private String getContent() {
         return getContent(getValidLuxThrottling(), getValidProxSensor(),
-                /* includeIdleMode= */ true, false);
+                /* includeIdleMode= */ true, /* enableEvenDimmer= */ false);
     }
 
     private String getContent(String brightnessCapConfig, String proxSensor,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index bf5a692..c70bf8a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2127,6 +2127,48 @@
     }
 
     @Test
+    public void testManualBrightness_stateDozePolicyOnUseNormalBrightnessForDozeTrue_brightnessDoze() {
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        when(mDisplayManagerFlagsMock.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+        float brightness = 0.277f;
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+        when(mHolder.hbmController.getCurrentBrightnessMax())
+                .thenReturn(PowerManager.BRIGHTNESS_MAX);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        // Start with state=DOZE.
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+        DisplayPowerRequest dprInit = new DisplayPowerRequest();
+        dprInit.policy = DisplayPowerRequest.POLICY_DOZE;
+        mHolder.dpc.requestPowerState(dprInit, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState; initialize to DOZE
+        // Go to state=ON. But state change would be blocked. so, state=DOZE.
+        when(mDisplayOffloadSession.blockScreenOn(any())).thenReturn(true);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.dozeScreenState = Display.STATE_ON;
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        dpr.useNormalBrightnessForDoze = true;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState; process turning on.
+
+        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+        listener.onBrightnessChanged(brightness);
+        advanceTime(1); // Send messages, run updatePowerState
+
+        // When state=DOZE, force doze brightness regardless the requested policy.
+        verify(mHolder.animator).animateTo(eq(brightness * DOZE_SCALE_FACTOR),
+                /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+                /* ignoreAnimationLimits= */ anyBoolean());
+    }
+
+    @Test
     public void testDozeManualBrightness_AbcIsNull() {
         when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 120cc84..f5bed99 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -29,8 +29,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -45,6 +47,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.util.Spline;
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.SurfaceControl;
@@ -59,6 +62,7 @@
 import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.display.LocalDisplayAdapter.BacklightAdapter;
+import com.android.server.display.color.ColorDisplayService;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.mode.DisplayModeDirector;
 import com.android.server.display.notifications.DisplayNotificationManager;
@@ -119,6 +123,8 @@
     private DisplayManagerFlags mFlags;
     @Mock
     private DisplayPowerController mMockedDisplayPowerController;
+    @Mock
+    private ColorDisplayService.ColorDisplayServiceInternal mMockedColorDisplayServiceInternal;
 
     private Handler mHandler;
 
@@ -133,6 +139,11 @@
     private Injector mInjector;
 
     @Mock
+    private DisplayDeviceConfig mMockDisplayDeviceConfig;
+    @Mock
+    private BacklightAdapter mMockBacklightAdapter;
+
+    @Mock
     private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy;
     private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
     private static final int[] BACKLIGHT_RANGE = { 1, 255 };
@@ -150,6 +161,9 @@
         doReturn(mMockedResources).when(mMockedContext).getResources();
         LocalServices.removeServiceForTest(LightsManager.class);
         LocalServices.addService(LightsManager.class, mMockedLightsManager);
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
+        LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+                mMockedColorDisplayServiceInternal);
         mInjector = new Injector();
         when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
         mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
@@ -211,7 +225,15 @@
         when(mMockedResources.getIntArray(
                 com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
                 .thenReturn(new int[]{});
+
+        when(mMockedColorDisplayServiceInternal.fetchEvenDimmerSpline(3)).thenReturn(
+                new Spline.LinearSpline(
+                        new float[]{2f, 3.0f, 500f, 2000f},
+                        new float[]{100, 0, 0, 0}));
+        when(mMockDisplayDeviceConfig.isEvenDimmerAvailable()).thenReturn(true);
+
         doReturn(true).when(mFlags).isDisplayOffloadEnabled();
+        doReturn(true).when(mFlags).isEvenDimmerEnabled();
         initDisplayOffloadSession();
     }
 
@@ -222,6 +244,122 @@
         }
     }
 
+    @Test
+    public void testEvenDimmer() throws InterruptedException {
+        // Set up
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+        // brightness|backlight| nits | strength
+        //  0.5      |   0.45  | 600  |  0     // initial setup value
+        //  0.4      |   0.35  | 500  |  0     // normal range value
+        //  0.31     |   0.2   |   3  |  0     // transition point
+        //  0.16     |  0.125  | 2.5  |  50    // mid point of even dimmer
+        //  0.1      |   0.05  |   2  |  100   // bottom of even dimmer range
+        //  0.05     |   0.01  |   1  |  100+ // beyond strength=100 range (should still return 100)
+        when(mMockDisplayDeviceConfig.getEvenDimmerTransitionPoint()).thenReturn(0.31f);
+        when(mMockDisplayDeviceConfig.getBacklightFromBrightness(0.5f)).thenReturn(0.45f);
+        when(mMockDisplayDeviceConfig.getBacklightFromBrightness(0.4f)).thenReturn(0.35f);
+        when(mMockDisplayDeviceConfig.getBacklightFromBrightness(0.31f)).thenReturn(0.2f);
+        when(mMockDisplayDeviceConfig.getBacklightFromBrightness(0.16f)).thenReturn(0.125f);
+        when(mMockDisplayDeviceConfig.getBacklightFromBrightness(0.1f)).thenReturn(0.05f);
+        when(mMockDisplayDeviceConfig.getBacklightFromBrightness(0.05f)).thenReturn(0.01f);
+        when(mMockDisplayDeviceConfig.getNitsFromBacklight(0.45f)).thenReturn(600f);
+        when(mMockDisplayDeviceConfig.getNitsFromBacklight(0.35f)).thenReturn(500f);
+        when(mMockDisplayDeviceConfig.getNitsFromBacklight(0.2f)).thenReturn(3f);
+        when(mMockDisplayDeviceConfig.getNitsFromBacklight(0.125f)).thenReturn(2.5f);
+        when(mMockDisplayDeviceConfig.getNitsFromBacklight(0.05f)).thenReturn(2f);
+        when(mMockDisplayDeviceConfig.getNitsFromBacklight(0.01f)).thenReturn(1f);
+
+        // initialise brightness to 0.5
+        Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON,
+                0.5f, 0.5f, null);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        verify(mSurfaceControlProxy).setDisplayPowerMode(any(), anyInt());
+        verify(mMockBacklightAdapter).setBacklight(anyFloat(), anyFloat(), anyFloat(), anyFloat());
+        verify(mMockedColorDisplayServiceInternal).applyEvenDimmerColorChanges(eq(false), eq(0));
+        verify(mMockedColorDisplayServiceInternal).fetchEvenDimmerSpline(eq(3.0f));
+
+        // set up normal brightness range
+        changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0.4f, 0.4f,
+                null);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        // verify normal brightness range
+        verify(mMockBacklightAdapter).setBacklight(0.35f, 500f, 0.35f, 500f);
+        verify(mMockedColorDisplayServiceInternal,
+                times(1)) // no more, since the strength is the same
+                .applyEvenDimmerColorChanges(eq(false), eq(0));
+        verify(mMockedColorDisplayServiceInternal, times(2)).fetchEvenDimmerSpline(eq(3.0f));
+
+        // set up even dimmer edge range
+        changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0.31f,
+                0.31f, null);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        // verify even dimmer edge range
+        verify(mMockBacklightAdapter).setBacklight(0.2f, 3f, 0.2f, 3f);
+        // verify no more times, since the strength and enabled-ness is the same
+        verify(mMockedColorDisplayServiceInternal, times(1)).applyEvenDimmerColorChanges(eq(false),
+                eq(0));
+        verify(mMockedColorDisplayServiceInternal, times(3)).fetchEvenDimmerSpline(eq(3.0f));
+
+        // set up mid point of even dimmer range
+        changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0.16f,
+                0.16f, null);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        // verify within even dimmer range
+        verify(mMockBacklightAdapter).setBacklight(0.125f, 2.5f, 0.125f, 2.5f);
+        verify(mMockedColorDisplayServiceInternal).applyEvenDimmerColorChanges(eq(true), eq(50));
+        verify(mMockedColorDisplayServiceInternal, times(4)).fetchEvenDimmerSpline(eq(3.0f));
+
+        // set up within even dimmer range
+        changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0.1f, 0.1f,
+                null);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        // verify within even dimmer range
+        verify(mMockBacklightAdapter).setBacklight(0.05f, 2f, 0.05f, 2f);
+        verify(mMockedColorDisplayServiceInternal).applyEvenDimmerColorChanges(eq(true), eq(100));
+        verify(mMockedColorDisplayServiceInternal, times(5)).fetchEvenDimmerSpline(eq(3.0f));
+
+        // set up below even dimmer range
+        changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0.05f,
+                0.05f, null);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        // verify within even dimmer range
+        verify(mMockBacklightAdapter).setBacklight(0.01f, 1f, 0.01f, 1f);
+        // ensure no greater than 100 strength is returned, therefore not called again.
+        verify(mMockedColorDisplayServiceInternal).applyEvenDimmerColorChanges(eq(true), eq(100));
+        verify(mMockedColorDisplayServiceInternal, times(6)).fetchEvenDimmerSpline(eq(3.0f));
+
+        // set up return to normal range
+        changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0.4f, 0.4f,
+                null);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        changeStateRunnable.run();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        // verify return to normal range
+        verify(mMockBacklightAdapter, times(2)).setBacklight(0.35f, 500f, 0.35f, 500f);
+        verify(mMockedColorDisplayServiceInternal, times(2)).applyEvenDimmerColorChanges(eq(false),
+                anyInt());
+        verify(mMockedColorDisplayServiceInternal, times(7)).fetchEvenDimmerSpline(eq(3.0f));
+    }
+
     /**
      * Confirm that display is marked as private when it is listed in
      * com.android.internal.R.array.config_localPrivateDisplayPorts.
@@ -1461,15 +1599,16 @@
             return mSurfaceControlProxy;
         }
 
-        // Instead of using DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay)
-        // we should use DisplayDeviceConfig.create(context, isFirstDisplay) for the test to ensure
-        // that real device DisplayDeviceConfig is not loaded for FakeDisplay and we are getting
-        // consistent behaviour. Please also note that context passed to this method, is
-        // mMockContext and values will be loaded from mMockResources.
         @Override
         public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
                 long physicalDisplayId, boolean isFirstDisplay, DisplayManagerFlags flags) {
-            return DisplayDeviceConfig.create(context, isFirstDisplay, flags);
+            return mMockDisplayDeviceConfig;
+        }
+
+        @Override
+        public BacklightAdapter getBacklightAdapter(IBinder displayToken, boolean isFirstDisplay,
+                LocalDisplayAdapter.SurfaceControlProxy surfaceControlProxy) {
+            return mMockBacklightAdapter;
         }
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
deleted file mode 100644
index 306b4f8..0000000
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display.brightness.clamper;
-
-import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_OFF;
-import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeClamper.BEDTIME_MODE_ON;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verify;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.provider.Settings;
-import android.testing.TestableContext;
-
-import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.android.internal.display.BrightnessSynchronizer;
-import com.android.server.testutils.TestHandler;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class BrightnessWearBedtimeModeClamperTest {
-
-    private static final float BRIGHTNESS_CAP = 0.3f;
-
-    @Mock
-    private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
-
-    @Rule
-    public final TestableContext mContext = new TestableContext(
-            InstrumentationRegistry.getInstrumentation().getContext());
-
-    private final TestHandler mTestHandler = new TestHandler(null);
-    private final TestInjector mInjector = new TestInjector();
-    private BrightnessWearBedtimeModeClamper mClamper;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mClamper = new BrightnessWearBedtimeModeClamper(mInjector, mTestHandler, mContext,
-                mMockClamperChangeListener, () -> BRIGHTNESS_CAP);
-        mTestHandler.flush();
-    }
-
-    @Test
-    public void testBrightnessCap() {
-        assertEquals(BRIGHTNESS_CAP, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
-    }
-
-    @Test
-    public void testBedtimeModeOn() {
-        setBedtimeModeEnabled(true);
-        assertTrue(mClamper.isActive());
-        verify(mMockClamperChangeListener).onChanged();
-    }
-
-    @Test
-    public void testBedtimeModeOff() {
-        setBedtimeModeEnabled(false);
-        assertFalse(mClamper.isActive());
-        verify(mMockClamperChangeListener).onChanged();
-    }
-
-    @Test
-    public void testType() {
-        assertEquals(BrightnessClamper.Type.WEAR_BEDTIME_MODE, mClamper.getType());
-    }
-
-    @Test
-    public void testOnDisplayChanged() {
-        float newBrightnessCap = 0.61f;
-
-        mClamper.onDisplayChanged(() -> newBrightnessCap);
-        mTestHandler.flush();
-
-        assertEquals(newBrightnessCap, mClamper.getBrightnessCap(), BrightnessSynchronizer.EPSILON);
-        verify(mMockClamperChangeListener).onChanged();
-    }
-
-    private void setBedtimeModeEnabled(boolean enabled) {
-        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE,
-                enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF);
-        mInjector.notifyBedtimeModeChanged();
-        mTestHandler.flush();
-    }
-
-    private static class TestInjector extends BrightnessWearBedtimeModeClamper.Injector {
-
-        private ContentObserver mObserver;
-
-        @Override
-        void registerBedtimeModeObserver(@NonNull ContentResolver cr,
-                @NonNull ContentObserver observer) {
-            mObserver = observer;
-        }
-
-        private void notifyBedtimeModeChanged() {
-            if (mObserver != null) {
-                mObserver.dispatchChange(/* selfChange= */ false,
-                        Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE));
-            }
-        }
-    }
-}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifierTest.java
new file mode 100644
index 0000000..8271a0f
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeModifierTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.server.display.brightness.clamper;
+
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeModifier.BEDTIME_MODE_OFF;
+import static com.android.server.display.brightness.clamper.BrightnessWearBedtimeModeModifier.BEDTIME_MODE_ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.testing.TestableContext;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.clamper.BrightnessClamperController.ModifiersAggregatedState;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class BrightnessWearBedtimeModeModifierTest {
+    private static final int NO_MODIFIER = 0;
+    private static final float BRIGHTNESS_CAP = 0.3f;
+    private static final String DISPLAY_ID = "displayId";
+
+    @Mock
+    private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+    @Mock
+    private DisplayManagerInternal.DisplayPowerRequest mMockRequest;
+    @Mock
+    private DisplayDeviceConfig mMockDisplayDeviceConfig;
+    @Mock
+    private IBinder mMockBinder;
+
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getContext());
+
+    private final TestHandler mTestHandler = new TestHandler(null);
+    private final TestInjector mInjector = new TestInjector();
+    private BrightnessWearBedtimeModeModifier mModifier;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mModifier = new BrightnessWearBedtimeModeModifier(mInjector, mTestHandler, mContext,
+                mMockClamperChangeListener, () -> BRIGHTNESS_CAP);
+        mTestHandler.flush();
+    }
+
+    @Test
+    public void testBedtimeModeOff() {
+        setBedtimeModeEnabled(false);
+        assertModifierState(
+                0.5f, true,
+                PowerManager.BRIGHTNESS_MAX, 0.5f,
+                false, true);
+        verify(mMockClamperChangeListener).onChanged();
+    }
+
+    @Test
+    public void testBedtimeModeOn() {
+        setBedtimeModeEnabled(true);
+        assertModifierState(
+                0.5f, true,
+                BRIGHTNESS_CAP, BRIGHTNESS_CAP,
+                true, false);
+        verify(mMockClamperChangeListener).onChanged();
+    }
+
+    @Test
+    public void testOnDisplayChanged() {
+        setBedtimeModeEnabled(true);
+        clearInvocations(mMockClamperChangeListener);
+        float newBrightnessCap = 0.61f;
+        onDisplayChange(newBrightnessCap);
+        mTestHandler.flush();
+
+        assertModifierState(
+                0.5f, true,
+                newBrightnessCap, 0.5f,
+                true, false);
+        verify(mMockClamperChangeListener).onChanged();
+    }
+
+    private void setBedtimeModeEnabled(boolean enabled) {
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE,
+                enabled ? BEDTIME_MODE_ON : BEDTIME_MODE_OFF);
+        mInjector.notifyBedtimeModeChanged();
+        mTestHandler.flush();
+    }
+
+    private void onDisplayChange(float brightnessCap) {
+        when(mMockDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode())
+                .thenReturn(brightnessCap);
+        mModifier.onDisplayChanged(ClamperTestUtilsKt.createDisplayDeviceData(
+                mMockDisplayDeviceConfig, mMockBinder, DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID));
+    }
+
+    private void assertModifierState(
+            float currentBrightness,
+            boolean currentSlowChange,
+            float maxBrightness, float brightness,
+            boolean isActive,
+            boolean isSlowChange) {
+        ModifiersAggregatedState modifierState = new ModifiersAggregatedState();
+        DisplayBrightnessState.Builder stateBuilder = DisplayBrightnessState.builder();
+        stateBuilder.setBrightness(currentBrightness);
+        stateBuilder.setIsSlowChange(currentSlowChange);
+
+        int maxBrightnessReason = isActive ? BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE
+                : BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+        int modifier = isActive ? BrightnessReason.MODIFIER_THROTTLED : NO_MODIFIER;
+
+        mModifier.applyStateChange(modifierState);
+        assertThat(modifierState.mMaxBrightness).isEqualTo(maxBrightness);
+        assertThat(modifierState.mMaxBrightnessReason).isEqualTo(maxBrightnessReason);
+
+        mModifier.apply(mMockRequest, stateBuilder);
+
+        assertThat(stateBuilder.getMaxBrightness())
+                .isWithin(BrightnessSynchronizer.EPSILON).of(maxBrightness);
+        assertThat(stateBuilder.getBrightness())
+                .isWithin(BrightnessSynchronizer.EPSILON).of(brightness);
+        assertThat(stateBuilder.getBrightnessMaxReason()).isEqualTo(maxBrightnessReason);
+        assertThat(stateBuilder.getBrightnessReason().getModifier()).isEqualTo(modifier);
+        assertThat(stateBuilder.isSlowChange()).isEqualTo(isSlowChange);
+    }
+
+
+    private static class TestInjector extends BrightnessWearBedtimeModeModifier.Injector {
+
+        private ContentObserver mObserver;
+
+        @Override
+        void registerBedtimeModeObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mObserver = observer;
+        }
+
+        private void notifyBedtimeModeChanged() {
+            if (mObserver != null) {
+                mObserver.dispatchChange(/* selfChange= */ false,
+                        Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE));
+            }
+        }
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 50f814d..efa8b3e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -397,9 +397,9 @@
         mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
                 allowAutoBrightnessWhileDozing, brightnessReason, policy,
                 useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
-        // 1st AUTO_BRIGHTNESS_MODE_DEFAULT
-        verify(mAutomaticBrightnessController).switchMode(
-                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
+        // 3rd AUTO_BRIGHTNESS_MODE_DOZE
+        verify(mAutomaticBrightnessController, times(3)).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE,
                 /* sendUpdate= */ false);
 
         // Validate interaction when automaticBrightnessController is in non-idle mode, display
@@ -407,8 +407,8 @@
         mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
                 allowAutoBrightnessWhileDozing, brightnessReason, policy,
                 useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
-        // 2nd AUTO_BRIGHTNESS_MODE_DEFAULT
-        verify(mAutomaticBrightnessController, times(2)).switchMode(
+        // AUTO_BRIGHTNESS_MODE_DEFAULT
+        verify(mAutomaticBrightnessController).switchMode(
                 AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
                 /* sendUpdate= */ false);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index 3dd2f24a..e863f15 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -40,7 +40,9 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.text.TextUtils;
 
 import com.android.internal.os.Clock;
@@ -87,6 +89,7 @@
     private static final String APP_1_PACKAGE_NAME = "com.android.test.stub1";
 
     @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     @Mock private AppOpsService mAppOpsService;
     @Mock private PackageManagerInternal mPackageManagerInt;
 
@@ -144,6 +147,7 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_APP_START_INFO_COMPONENT)
     public void testApplicationStartInfo() throws Exception {
         // Make sure we can write to the file.
         assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir));
@@ -167,7 +171,7 @@
         ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
 
         // Case 1: Activity start intent failed
-        mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
+        mAppStartInfoTracker.onActivityIntentStarted(buildIntent(COMPONENT),
                 appStartTimestampIntentStarted);
         mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(1);
@@ -185,7 +189,7 @@
                 ApplicationStartInfo.START_TYPE_UNSET,                // state type
                 ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
 
-        mAppStartInfoTracker.onIntentFailed(appStartTimestampIntentStarted);
+        mAppStartInfoTracker.onActivityIntentFailed(appStartTimestampIntentStarted);
         list.clear();
         mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(0);
@@ -194,7 +198,7 @@
         mAppStartInfoTracker.clearProcessStartInfo(true);
 
         // Case 2: Activity start launch cancelled
-        mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
+        mAppStartInfoTracker.onActivityIntentStarted(buildIntent(COMPONENT),
                 appStartTimestampIntentStarted);
         list.clear();
         mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
@@ -236,12 +240,13 @@
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_ERROR,             // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
-                ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
+                ApplicationStartInfo.LAUNCH_MODE_STANDARD,            // launch mode
+                ApplicationStartInfo.START_COMPONENT_ACTIVITY);       // start component
 
         mAppStartInfoTracker.clearProcessStartInfo(true);
 
         // Case 3: Activity start success
-        mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
+        mAppStartInfoTracker.onActivityIntentStarted(buildIntent(COMPONENT),
                 appStartTimestampIntentStarted);
         list.clear();
         mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
@@ -255,6 +260,7 @@
         verifyInProgressRecordsSize(1);
         assertEquals(list.size(), 1);
 
+        // The records will now be in both backing data structures, so verify in each.
         verifyInProgressApplicationStartInfo(
                 0,                                                    // index
                 APP_1_PID_1,                                          // pid
@@ -277,7 +283,8 @@
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
-                ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
+                ApplicationStartInfo.LAUNCH_MODE_STANDARD,            // launch mode
+                ApplicationStartInfo.START_COMPONENT_ACTIVITY);       // start component
 
         mAppStartInfoTracker.onActivityLaunchFinished(appStartTimestampIntentStarted, COMPONENT,
                 appStartTimestampActivityLaunchFinished, ApplicationStartInfo.LAUNCH_MODE_STANDARD);
@@ -300,7 +307,7 @@
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
                 ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
 
-        mAppStartInfoTracker.onReportFullyDrawn(appStartTimestampIntentStarted,
+        mAppStartInfoTracker.onActivityReportFullyDrawn(appStartTimestampIntentStarted,
                 appStartTimestampReportFullyDrawn);
         list.clear();
         mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
@@ -317,7 +324,8 @@
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
-                ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
+                ApplicationStartInfo.LAUNCH_MODE_STANDARD,            // launch mode
+                ApplicationStartInfo.START_COMPONENT_ACTIVITY);       // start component
 
         // Don't clear records for use in subsequent cases.
 
@@ -347,7 +355,8 @@
                 ApplicationStartInfo.START_REASON_SERVICE,            // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
-                ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
+                ApplicationStartInfo.LAUNCH_MODE_STANDARD,            // launch mode
+                ApplicationStartInfo.START_COMPONENT_SERVICE);        // start component
 
         // Case 5: Create an instance of app1 with a different user started for a broadcast
         sleep(1);
@@ -376,7 +385,8 @@
                 ApplicationStartInfo.START_REASON_BROADCAST,          // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
-                ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
+                ApplicationStartInfo.LAUNCH_MODE_STANDARD,            // launch mode
+                ApplicationStartInfo.START_COMPONENT_BROADCAST);      // start component
 
         // Case 6: User 2 gets removed
         mAppStartInfoTracker.onPackageRemoved(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, false);
@@ -422,7 +432,9 @@
                 ApplicationStartInfo.START_REASON_CONTENT_PROVIDER,   // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
-                ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
+                ApplicationStartInfo.LAUNCH_MODE_STANDARD,            // launch mode
+                ApplicationStartInfo.START_COMPONENT_CONTENT_PROVIDER // start component
+        );
 
         // Case 8: Save and load again
         ArrayList<ApplicationStartInfo> original = new ArrayList<ApplicationStartInfo>();
@@ -453,6 +465,7 @@
      */
     @SuppressWarnings("GuardedBy")
     @Test
+    @EnableFlags(android.app.Flags.FLAG_APP_START_INFO_COMPONENT)
     public void testInProgressRecordsLimit() throws Exception {
         ProcessRecord app = makeProcessRecord(
                 APP_1_PID_1,                 // pid
@@ -466,7 +479,7 @@
         // never exceeds the expected size of MAX_IN_PROGRESS_RECORDS.
         for (int i = 0; i < AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS * 2; i++) {
             Long startTime = Long.valueOf(i);
-            mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), startTime);
+            mAppStartInfoTracker.onActivityIntentStarted(buildIntent(COMPONENT), startTime);
             verifyInProgressRecordsSize(
                     Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS));
 
@@ -509,8 +522,12 @@
 
         mAppStartInfoTracker.handleProcessBroadcastStart(3, app, buildIntent(COMPONENT),
                 false /* isAlarm */);
+        // Add a brief delay between timestamps to make sure the clock, which is in milliseconds has
+        // actually incremented.
+        sleep(1);
         mAppStartInfoTracker.handleProcessBroadcastStart(2, app, buildIntent(COMPONENT),
                 false /* isAlarm */);
+        sleep(1);
         mAppStartInfoTracker.handleProcessBroadcastStart(1, app, buildIntent(COMPONENT),
                 false /* isAlarm */);
 
@@ -557,9 +574,10 @@
         // Now load from disk.
         mAppStartInfoTracker.loadExistingProcessStartInfo();
 
-        // Confirm clock has been set and that its current time is greater than the previous one.
+        // Confirm clock has been set and that its current time is greater than or equal to the
+        // previous one, thereby ensuring it was loaded from disk.
         assertNotNull(mAppStartInfoTracker.mMonotonicClock);
-        assertTrue(mAppStartInfoTracker.mMonotonicClock.monotonicTime() > originalMonotonicTime);
+        assertTrue(mAppStartInfoTracker.mMonotonicClock.monotonicTime() >= originalMonotonicTime);
     }
 
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
@@ -613,6 +631,10 @@
         }
     }
 
+    /**
+     * Convenience helper to access the record from the in progress data structure. Only applies for
+     * activity starts.
+     */
     private void verifyInProgressApplicationStartInfo(int index,
             Integer pid, Integer uid, Integer packageUid,
             Integer definingUid, String processName,
@@ -620,14 +642,15 @@
         synchronized (mAppStartInfoTracker.mLock) {
             verifyApplicationStartInfo(mAppStartInfoTracker.mInProgressRecords.valueAt(index),
                     pid, uid, packageUid, definingUid, processName, reason, startupState,
-                    startType, launchMode);
+                    startType, launchMode, ApplicationStartInfo.START_COMPONENT_ACTIVITY);
         }
     }
 
     private void verifyApplicationStartInfo(ApplicationStartInfo info,
             Integer pid, Integer uid, Integer packageUid,
             Integer definingUid, String processName,
-            Integer reason, Integer startupState, Integer startType, Integer launchMode) {
+            Integer reason, Integer startupState, Integer startType, Integer launchMode,
+            Integer startComponent) {
         assertNotNull(info);
 
         if (pid != null) {
@@ -657,6 +680,9 @@
         if (launchMode != null) {
             assertEquals(launchMode.intValue(), info.getLaunchMode());
         }
+        if (startComponent != null) {
+            assertEquals(startComponent.intValue(), info.getStartComponent());
+        }
     }
 
     private class TestInjector extends Injector {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
index 93066d8..67475335 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
@@ -213,7 +213,7 @@
                 any(), any(), any(),
                 any(), any(),
                 any(), any(),
-                any(), any(),
+                any(), any(), any(),
                 anyLong(), anyLong());
 
         final ProcessRecord r = spy(new ProcessRecord(mAms, ai, ai.processName, ai.uid));
@@ -277,7 +277,7 @@
                 null, null,
                 null,
                 null, null, null,
-                null, null, null,
+                null, null, null, null,
                 0, 0);
 
         // Sleep until timeout should have triggered
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 f6ad07d..2107406 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -229,8 +229,10 @@
         doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
         doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
         setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
-        doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
-                .enqueueProcessChangeItemLocked(anyInt(), anyInt());
+        doNothing().when(pr).enqueueProcessChangeItemLocked(anyInt(), anyInt(), anyInt(),
+                anyInt());
+        doNothing().when(pr).enqueueProcessChangeItemLocked(anyInt(), anyInt(), anyInt(),
+                anyBoolean());
         mService.mOomAdjuster = mService.mConstants.ENABLE_NEW_OOMADJ
                 ? new OomAdjusterModernImpl(mService, mService.mProcessList,
                         new ActiveUids(mService, false), mInjector)
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
index 014b98c..43becc5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
@@ -216,7 +216,7 @@
                 any(), any(), any(),
                 any(), any(),
                 any(), any(),
-                any(), any(),
+                any(), any(), any(),
                 anyLong(), anyLong());
         final ProcessRecord r = spy(new ProcessRecord(mAms, ai, ai.processName, ai.uid));
         r.setPid(myPid());
@@ -265,7 +265,7 @@
                 null, null,
                 null,
                 null, null, null,
-                null, null, null,
+                null, null, null, null,
                 0, 0);
         return app;
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 3062d51..9ba2724 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -172,6 +172,7 @@
         mBackgroundUserSoundNotifier.muteAlarmSounds(mSpiedContext);
 
         verify(apc1.getPlayerProxy()).stop();
+        verify(mockAudioPolicy).sendFocusLossAndUpdate(afi);
         verify(apc2.getPlayerProxy(), never()).stop();
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index 6e41685..e0c7bfe 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
@@ -36,6 +37,7 @@
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
@@ -47,6 +49,7 @@
 import android.os.SystemProperties;
 import android.platform.test.flag.junit.SetFlagsRule;
 
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -145,6 +148,22 @@
                 }
         ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean()));
 
+        try {
+            when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> {
+                final PackageInfo res = new PackageInfo();
+                res.packageName = inv.getArgument(0);
+                res.setApexPackageName(res.packageName);
+                return res;
+            });
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        Context testContext = InstrumentationRegistry.getInstrumentation()
+                .getTargetContext();
+        when(mMockContext.getUser()).thenReturn(testContext.getUser());
+        when(mMockContext.getPackageName()).thenReturn(testContext.getPackageName());
+
         SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(false));
     }
 
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 0b762df..9983fb4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -32,6 +32,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -50,6 +52,7 @@
 
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.Flags;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.content.ComponentName;
@@ -64,7 +67,10 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 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.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.WallpaperService;
@@ -91,8 +97,10 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -100,6 +108,7 @@
 import org.mockito.quality.Strictness;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -125,6 +134,7 @@
     @ClassRule
     public static final TestableContext sContext = new TestableContext(
             InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
     private static ComponentName sImageWallpaperComponentName;
     private static ComponentName sDefaultWallpaperComponent;
 
@@ -133,8 +143,11 @@
     @Mock
     private DisplayManager mDisplayManager;
 
+    private final TemporaryFolder mFolder = new TemporaryFolder();
+    private final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     @Rule
-    public final TemporaryFolder mFolder = new TemporaryFolder();
+    public RuleChain rules = RuleChain.outerRule(mSetFlagsRule).around(mFolder);
+
     private final SparseArray<File> mTempDirs = new SparseArray<>();
     private WallpaperManagerService mService;
     private static IWallpaperConnection.Stub sWallpaperService;
@@ -325,6 +338,7 @@
      * is issued to the wallpaper.
      */
     @Test
+    @Ignore("b/368345733")
     public void testSetCurrentComponent() throws Exception {
         final int testUserId = USER_SYSTEM;
         mService.switchUser(testUserId, null);
@@ -411,26 +425,84 @@
     }
 
     @Test
-    public void testXmlSerializationRoundtrip() {
-        WallpaperData systemWallpaperData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
+    @EnableFlags(Flags.FLAG_REMOVE_NEXT_WALLPAPER_COMPONENT)
+    public void testSaveLoadSettings() {
+        WallpaperData expectedData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
+        expectedData.setComponent(sDefaultWallpaperComponent);
+        expectedData.primaryColors = new WallpaperColors(Color.valueOf(Color.RED),
+                Color.valueOf(Color.BLUE), null);
+        expectedData.mWallpaperDimAmount = 0.5f;
+        expectedData.mUidToDimAmount.put(0, 0.5f);
+        expectedData.mUidToDimAmount.put(1, 0.4f);
+
+        ByteArrayOutputStream ostream = new ByteArrayOutputStream();
         try {
             TypedXmlSerializer serializer = Xml.newBinarySerializer();
-            serializer.setOutput(new ByteArrayOutputStream(), StandardCharsets.UTF_8.name());
-            serializer.startDocument(StandardCharsets.UTF_8.name(), true);
-            mService.mWallpaperDataParser.writeWallpaperAttributes(
-                    serializer, "wp", systemWallpaperData);
+            serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
+            mService.mWallpaperDataParser.saveSettingsToSerializer(serializer, expectedData, null);
+            ostream.close();
+        } catch (IOException e) {
+            fail("exception occurred while writing system wallpaper attributes");
+        }
+
+        WallpaperData actualData = new WallpaperData(0, FLAG_SYSTEM);
+        try {
+            ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
+            TypedXmlPullParser parser = Xml.newBinaryPullParser();
+            parser.setInput(istream, StandardCharsets.UTF_8.name());
+            mService.mWallpaperDataParser.loadSettingsFromSerializer(parser,
+                    actualData, /* userId= */0, /* loadSystem= */ true, /* loadLock= */
+                    false, /* keepDimensionHints= */ true,
+                    new WallpaperDisplayHelper.DisplayData(0));
+        } catch (IOException | XmlPullParserException e) {
+            fail("exception occurred while parsing wallpaper");
+        }
+
+        assertThat(actualData.getComponent()).isEqualTo(expectedData.getComponent());
+        assertThat(actualData.primaryColors).isEqualTo(expectedData.primaryColors);
+        assertThat(actualData.mWallpaperDimAmount).isEqualTo(expectedData.mWallpaperDimAmount);
+        assertThat(actualData.mUidToDimAmount).isNotNull();
+        assertThat(actualData.mUidToDimAmount.size()).isEqualTo(
+                expectedData.mUidToDimAmount.size());
+        for (int i = 0; i < actualData.mUidToDimAmount.size(); i++) {
+            int key = actualData.mUidToDimAmount.keyAt(0);
+            assertThat(actualData.mUidToDimAmount.get(key)).isEqualTo(
+                    expectedData.mUidToDimAmount.get(key));
+        }
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_REMOVE_NEXT_WALLPAPER_COMPONENT)
+    public void testSaveLoadSettings_legacyNextComponent() {
+        WallpaperData systemWallpaperData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
+        systemWallpaperData.setComponent(sDefaultWallpaperComponent);
+        ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+        try {
+            TypedXmlSerializer serializer = Xml.newBinarySerializer();
+            serializer.setOutput(ostream, StandardCharsets.UTF_8.name());
+            mService.mWallpaperDataParser.saveSettingsToSerializer(serializer, systemWallpaperData,
+                    null);
+            ostream.close();
         } catch (IOException e) {
             fail("exception occurred while writing system wallpaper attributes");
         }
 
         WallpaperData shouldMatchSystem = new WallpaperData(0, FLAG_SYSTEM);
         try {
+            ByteArrayInputStream istream = new ByteArrayInputStream(ostream.toByteArray());
             TypedXmlPullParser parser = Xml.newBinaryPullParser();
-            mService.mWallpaperDataParser.parseWallpaperAttributes(parser, shouldMatchSystem, true);
-        } catch (XmlPullParserException e) {
+            parser.setInput(istream, StandardCharsets.UTF_8.name());
+            mService.mWallpaperDataParser.loadSettingsFromSerializer(parser,
+                    shouldMatchSystem, /* userId= */0, /* loadSystem= */ true, /* loadLock= */
+                    false, /* keepDimensionHints= */ true,
+                    new WallpaperDisplayHelper.DisplayData(0));
+        } catch (IOException | XmlPullParserException e) {
             fail("exception occurred while parsing wallpaper");
         }
-        assertEquals(systemWallpaperData.primaryColors, shouldMatchSystem.primaryColors);
+
+        assertThat(shouldMatchSystem.nextWallpaperComponent).isEqualTo(
+                systemWallpaperData.getComponent());
+        assertThat(shouldMatchSystem.primaryColors).isEqualTo(systemWallpaperData.primaryColors);
     }
 
     @Test
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index fc4d8d8..0702926 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.power;
 
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
@@ -31,11 +34,13 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.SensorManager;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.DisplayManagerInternal;
 import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.IWakeLockCallback;
@@ -48,11 +53,18 @@
 import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.testing.TestableContext;
+import android.util.IntArray;
+import android.util.SparseBooleanArray;
+import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayInfo;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
+import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.power.batterysaver.BatterySaverStateMachine;
 import com.android.server.power.feature.PowerManagerFlags;
@@ -71,6 +83,8 @@
 public class NotifierTest {
     private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
     private static final int USER_ID = 0;
+    private static final int DISPLAY_PORT = 0xFF;
+    private static final long DISPLAY_MODEL = 0xEEEEEEEEL;
 
     @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
     @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
@@ -81,10 +95,16 @@
     @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
     @Mock private Vibrator mVibrator;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private InputManagerInternal mInputManagerInternal;
+    @Mock private InputMethodManagerInternal mInputMethodManagerInternal;
+    @Mock private DisplayManagerInternal mDisplayManagerInternal;
+    @Mock private ActivityManagerInternal mActivityManagerInternal;
     @Mock private WakeLockLog mWakeLockLog;
 
     @Mock private IBatteryStats mBatteryStats;
 
+    @Mock private WindowManagerPolicy mPolicy;
+
     @Mock private PowerManagerFlags mPowerManagerFlags;
 
     @Mock private AppOpsManager mAppOpsManager;
@@ -96,6 +116,8 @@
     private FakeExecutor mTestExecutor = new FakeExecutor();
     private Notifier mNotifier;
 
+    private DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -103,11 +125,25 @@
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
 
+        LocalServices.removeServiceForTest(InputManagerInternal.class);
+        LocalServices.addService(InputManagerInternal.class, mInputManagerInternal);
+        LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
+        LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal);
+
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal);
+
+        mDefaultDisplayInfo.address = DisplayAddress.fromPortAndModel(DISPLAY_PORT, DISPLAY_MODEL);
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternal);
+
         mContextSpy = spy(new TestableContext(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
         when(mContextSpy.getSystemService(Vibrator.class)).thenReturn(mVibrator);
+        when(mDisplayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(
+                mDefaultDisplayInfo);
 
         mService = new PowerManagerService(mContextSpy, mInjector);
     }
@@ -232,6 +268,32 @@
     }
 
     @Test
+    public void testOnGlobalWakefulnessChangeStarted() throws Exception {
+        createNotifier();
+        // GIVEN system is currently non-interactive
+        when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(false);
+        final int displayId1 = 101;
+        final int displayId2 = 102;
+        final int[] displayIds = new int[]{displayId1, displayId2};
+        when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displayIds));
+        mNotifier.onGlobalWakefulnessChangeStarted(WAKEFULNESS_ASLEEP,
+                PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, /* eventTime= */ 1000);
+        mTestLooper.dispatchAll();
+
+        // WHEN a global wakefulness change to interactive starts
+        mNotifier.onGlobalWakefulnessChangeStarted(WAKEFULNESS_AWAKE,
+                PowerManager.WAKE_REASON_TAP, /* eventTime= */ 2000);
+        mTestLooper.dispatchAll();
+
+        // THEN input is notified of all displays being interactive
+        final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray();
+        expectedDisplayInteractivities.put(displayId1, true);
+        expectedDisplayInteractivities.put(displayId2, true);
+        verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities);
+        verify(mInputMethodManagerInternal).setInteractive(/* interactive= */ true);
+    }
+
+    @Test
     public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException {
         when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
         createNotifier();
@@ -551,7 +613,7 @@
                 mContextSpy,
                 mBatteryStats,
                 mInjector.createSuspendBlocker(mService, "testBlocker"),
-                null,
+                mPolicy,
                 null,
                 null,
                 mTestExecutor, mPowerManagerFlags, injector);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index b58c28b..54a02cf 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -268,6 +268,10 @@
 
         mClock = new OffsettableClock.Stopped();
         mTestLooper = new TestLooper(mClock::now);
+        DisplayInfo displayInfo = Mockito.mock(DisplayInfo.class);
+        displayInfo.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY))
+                .thenReturn(displayInfo);
     }
 
     private PowerManagerService createService() {
@@ -794,6 +798,57 @@
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
     }
 
+    @Test
+    public void testWakefulnessPerGroup_IPowerManagerWakeUpWithDisplayId() {
+        final int nonDefaultPowerGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+        int displayInNonDefaultGroup = 1;
+        final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+                new AtomicReference<>();
+        long eventTime1 = 10;
+        long eventTime2 = eventTime1 + 1;
+        long eventTime3 = eventTime2 + 1;
+        doAnswer((Answer<Void>) invocation -> {
+            listener.set(invocation.getArgument(0));
+            return null;
+        }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+
+        createService();
+        startSystem();
+        listener.get().onDisplayGroupAdded(nonDefaultPowerGroupId);
+
+        // Verify the global wakefulness is AWAKE
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        // Transition default display to doze, and verify the global wakefulness
+        mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_DOZING, eventTime1,
+                0, PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, 0, null, null);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+        // Transition the display from non default power group to doze, and verify the change in
+        // the global wakefulness
+        mService.setWakefulnessLocked(nonDefaultPowerGroupId, WAKEFULNESS_DOZING, eventTime2,
+                0, PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+        assertThat(mService.getWakefulnessLocked(nonDefaultPowerGroupId))
+                .isEqualTo(WAKEFULNESS_DOZING);
+
+        // Wakeup the display from the non default power group
+        DisplayInfo displayInfo = Mockito.mock(DisplayInfo.class);
+        displayInfo.displayGroupId = nonDefaultPowerGroupId;
+        when(mDisplayManagerInternalMock.getDisplayInfo(displayInNonDefaultGroup))
+                .thenReturn(displayInfo);
+        mClock.fastForward(eventTime3);
+        mService.getBinderServiceInstance().wakeUpWithDisplayId(eventTime3,
+                PowerManager.WAKE_REASON_APPLICATION, "testing IPowerManager.wakeUp()",
+                "pkg.name", displayInNonDefaultGroup);
+
+        assertThat(mService.getWakefulnessLocked(nonDefaultPowerGroupId))
+                .isEqualTo(WAKEFULNESS_AWAKE);
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+        assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY))
+                .isEqualTo(WAKEFULNESS_DOZING);
+    }
+
     /**
      * Tests a series of variants that control whether a device wakes-up when it is plugged in
      * or docked.
diff --git a/services/tests/powerstatstests/res/xml/irq_device_map_3.xml b/services/tests/powerstatstests/res/xml/irq_device_map_3.xml
index fd55428..c3df078 100644
--- a/services/tests/powerstatstests/res/xml/irq_device_map_3.xml
+++ b/services/tests/powerstatstests/res/xml/irq_device_map_3.xml
@@ -32,4 +32,7 @@
     <device name="test.sensor.device">
         <subsystem>Sensor</subsystem>
     </device>
+    <device name="test.bluetooth.device">
+        <subsystem>Bluetooth</subsystem>
+    </device>
 </irq-device-map>
\ No newline at end of file
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index 1d20538..c037f97 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -927,7 +927,7 @@
         assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
 
         mBatteryStatsImpl.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider,
-                mPowerStatsStore);
+                mPowerStatsStore, /* accumulateBatteryUsageStats */ false);
 
         synchronized (mBatteryStatsImpl) {
             mBatteryStatsImpl.noteFlashlightOnLocked(42, mMockClock.realtime, mMockClock.uptime);
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 fde84e9..0e60156 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
@@ -419,7 +419,8 @@
                 mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
                 mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock);
 
-        batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore);
+        batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore,
+                /* accumulateBatteryUsageStats */ false);
         synchronized (batteryStats) {
             batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
         }
@@ -505,6 +506,102 @@
                 .of(180.0);
     }
 
+    @Test
+    public void accumulateBatteryUsageStats() {
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        setTime(5 * MINUTE_IN_MS);
+
+        // Capture the session start timestamp
+        synchronized (batteryStats) {
+            batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        }
+
+        PowerStatsStore powerStatsStore = new PowerStatsStore(
+                new File(mStatsRule.getHistoryDir(), getClass().getSimpleName()),
+                mStatsRule.getHandler());
+        powerStatsStore.reset();
+
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock);
+
+        batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore,
+                /* accumulateBatteryUsageStats */ true);
+
+        synchronized (batteryStats) {
+            batteryStats.noteFlashlightOnLocked(APP_UID,
+                    10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+        }
+        synchronized (batteryStats) {
+            batteryStats.noteFlashlightOffLocked(APP_UID,
+                    20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+        }
+
+        synchronized (batteryStats) {
+            batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        }
+
+        synchronized (batteryStats) {
+            batteryStats.noteFlashlightOnLocked(APP_UID,
+                    30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+        }
+        synchronized (batteryStats) {
+            batteryStats.noteFlashlightOffLocked(APP_UID,
+                    50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
+        }
+        setTime(55 * MINUTE_IN_MS);
+        synchronized (batteryStats) {
+            batteryStats.resetAllStatsAndHistoryLocked(BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
+        }
+
+        // This section has not been saved yet, but should be added to the accumulated totals
+        synchronized (batteryStats) {
+            batteryStats.noteFlashlightOnLocked(APP_UID,
+                    80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+        }
+        synchronized (batteryStats) {
+            batteryStats.noteFlashlightOffLocked(APP_UID,
+                    110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
+        }
+        setTime(115 * MINUTE_IN_MS);
+
+        // Await completion
+        ConditionVariable done = new ConditionVariable();
+        mStatsRule.getHandler().post(done::open);
+        done.block();
+
+        BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats,
+                new BatteryUsageStatsQuery.Builder().accumulated().build());
+
+        assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+        assertThat(stats.getStatsEndTimestamp()).isEqualTo(115 * MINUTE_IN_MS);
+
+        // Section 1 (saved): 20 - 10 = 10
+        // Section 2 (saved): 50 - 30 = 20
+        // Section 3 (fresh): 110 - 80 = 30
+        // Total: 10 + 20 + 30 = 60
+        assertThat(stats.getAggregateBatteryConsumer(
+                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+                .isWithin(0.0001)
+                .of(360.0);  // 360 mA * 1.0 hour
+        assertThat(stats.getAggregateBatteryConsumer(
+                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+                .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+                .isEqualTo(60 * MINUTE_IN_MS);
+
+        final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
+                .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
+        assertThat(uidBatteryConsumer
+                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+                .isWithin(0.1)
+                .of(360.0);
+        assertThat(uidBatteryConsumer
+                .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+                .isEqualTo(60 * MINUTE_IN_MS);
+    }
+
     private void setTime(long timeMs) {
         mMockClock.currentTime = timeMs;
         mMockClock.realtime = timeMs;
@@ -550,7 +647,8 @@
             return null;
         }).when(powerStatsStore).storeBatteryUsageStats(anyLong(), any());
 
-        mStatsRule.getBatteryStats().saveBatteryUsageStatsOnReset(provider, powerStatsStore);
+        mStatsRule.getBatteryStats().saveBatteryUsageStatsOnReset(provider, powerStatsStore,
+                /* accumulateBatteryUsageStats */ false);
 
         // Make an incompatible change of supported energy components.  This will trigger
         // a BatteryStats reset, which will generate a snapshot of battery stats.
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
index 0dc836b..fe4d971 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
@@ -17,6 +17,7 @@
 package com.android.server.power.stats.wakeups;
 
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_BLUETOOTH;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
@@ -52,6 +53,7 @@
     private static final String KERNEL_REASON_SOUND_TRIGGER_IRQ = "129 test.sound_trigger.device";
     private static final String KERNEL_REASON_SENSOR_IRQ = "15 test.sensor.device";
     private static final String KERNEL_REASON_CELLULAR_DATA_IRQ = "18 test.cellular_data.device";
+    private static final String KERNEL_REASON_BLUETOOTH_IRQ = "19 test.bluetooth.device";
     private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
     private static final String KERNEL_REASON_UNKNOWN_FORMAT = "free-form-reason test.alarm.device";
     private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device";
@@ -62,12 +64,14 @@
     private static final int TEST_UID_3 = 92261423;
     private static final int TEST_UID_4 = 56926423;
     private static final int TEST_UID_5 = 76421423;
+    private static final int TEST_UID_6 = 62345353;
 
     private static final int TEST_PROC_STATE_1 = 72331;
     private static final int TEST_PROC_STATE_2 = 792351;
     private static final int TEST_PROC_STATE_3 = 138831;
     private static final int TEST_PROC_STATE_4 = 23231;
     private static final int TEST_PROC_STATE_5 = 42;
+    private static final int TEST_PROC_STATE_6 = 129942;
 
     private static final Context sContext = InstrumentationRegistry.getTargetContext();
     private final Handler mHandler = Mockito.mock(Handler.class);
@@ -79,6 +83,7 @@
         obj.mUidProcStates.put(TEST_UID_3, TEST_PROC_STATE_3);
         obj.mUidProcStates.put(TEST_UID_4, TEST_PROC_STATE_4);
         obj.mUidProcStates.put(TEST_UID_5, TEST_PROC_STATE_5);
+        obj.mUidProcStates.put(TEST_UID_6, TEST_PROC_STATE_6);
     }
 
     @Test
@@ -118,6 +123,7 @@
                 CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
                 CPU_WAKEUP_SUBSYSTEM_SENSOR,
                 CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA,
+                CPU_WAKEUP_SUBSYSTEM_BLUETOOTH,
         };
 
         final String[] kernelReasons = new String[] {
@@ -126,10 +132,11 @@
                 KERNEL_REASON_SOUND_TRIGGER_IRQ,
                 KERNEL_REASON_SENSOR_IRQ,
                 KERNEL_REASON_CELLULAR_DATA_IRQ,
+                KERNEL_REASON_BLUETOOTH_IRQ,
         };
 
         final int[] uids = new int[] {
-                TEST_UID_2, TEST_UID_3, TEST_UID_4, TEST_UID_1, TEST_UID_5
+                TEST_UID_2, TEST_UID_3, TEST_UID_4, TEST_UID_1, TEST_UID_5, TEST_UID_6
         };
 
         final int[] procStates = new int[] {
@@ -137,7 +144,8 @@
                 TEST_PROC_STATE_3,
                 TEST_PROC_STATE_4,
                 TEST_PROC_STATE_1,
-                TEST_PROC_STATE_5
+                TEST_PROC_STATE_5,
+                TEST_PROC_STATE_6
         };
 
         final int total = subsystems.length;
@@ -285,6 +293,40 @@
     }
 
     @Test
+    public void bluetoothIrqAttributionSolo() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+        final long wakeupTime = 1236121;
+
+        populateDefaultProcStates(obj);
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_BLUETOOTH_IRQ);
+
+        // Outside the window, so should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH,
+                wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH,
+                wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
+        // Should be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH, wakeupTime + 5, TEST_UID_3,
+                TEST_UID_5);
+
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution).isNotNull();
+        assertThat(attribution.size()).isEqualTo(1);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).indexOfKey(
+                TEST_UID_1)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).indexOfKey(
+                TEST_UID_2)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).get(TEST_UID_3)).isEqualTo(
+                TEST_PROC_STATE_3);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).indexOfKey(
+                TEST_UID_4)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
+    }
+
+    @Test
     public void alarmAndWifiIrqAttribution() {
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 92123210;
@@ -400,6 +442,47 @@
     }
 
     @Test
+    public void unknownAndBluetoothAttribution() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+        final long wakeupTime = 92123520;
+
+        populateDefaultProcStates(obj);
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 24,
+                KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_BLUETOOTH_IRQ);
+
+        // Bluetooth activity
+        // Outside the window, so should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH,
+                wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_4);
+        // Should be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH, wakeupTime + 2, TEST_UID_1);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH, wakeupTime - 1, TEST_UID_3,
+                TEST_UID_5);
+
+        // Unrelated, should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution).isNotNull();
+        assertThat(attribution.size()).isEqualTo(2);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).get(TEST_UID_1)).isEqualTo(
+                TEST_PROC_STATE_1);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH)
+                .indexOfKey(TEST_UID_2)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).get(TEST_UID_3)).isEqualTo(
+                TEST_PROC_STATE_3);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH)
+                .indexOfKey(TEST_UID_4)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_BLUETOOTH).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isNull();
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isFalse();
+    }
+
+    @Test
     public void unknownFormatWakeupIgnored() {
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 72123210;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index bbe0755..cbe6700 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -52,8 +52,10 @@
         "services.credentials",
         "services.devicepolicy",
         "services.flags",
+        "com.android.server.flags.services-aconfig-java",
         "services.net",
         "services.people",
+        "services.supervision",
         "services.usage",
         "service-permission.stubs.system_server",
         "guava",
@@ -80,6 +82,7 @@
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
+        "flag-junit",
         "junit",
         "junit-params",
         "ActivityContext",
diff --git a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
index ec78bce..c18faef 100644
--- a/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/PinnerServiceTest.java
@@ -31,6 +31,9 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfigInterface;
 import android.testing.TestableContext;
@@ -43,6 +46,9 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.flags.Flags;
+import com.android.server.pinner.PinnedFile;
+import com.android.server.pinner.PinnerService;
 import com.android.server.testutils.FakeDeviceConfigInterface;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -73,15 +79,18 @@
 
     private static final long WAIT_FOR_PINNER_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
 
+    private static final int MEMORY_PERCENTAGE_FOR_QUOTA = 10;
+
     @Rule
     public TestableContext mContext =
             new TestableContext(InstrumentationRegistry.getContext(), null);
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private final ArraySet<String> mUpdatedPackages = new ArraySet<>();
     private ResolveInfo mHomePackageResolveInfo;
     private FakeDeviceConfigInterface mFakeDeviceConfigInterface;
     private PinnerService.Injector mInjector;
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -114,6 +123,8 @@
         resources.addOverride(com.android.internal.R.bool.config_pinnerCameraApp, false);
         resources.addOverride(com.android.internal.R.integer.config_pinnerHomePinBytes, 0);
         resources.addOverride(com.android.internal.R.bool.config_pinnerAssistantApp, false);
+        resources.addOverride(com.android.internal.R.integer.config_pinnerMaxPinnedMemoryPercentage,
+                MEMORY_PERCENTAGE_FOR_QUOTA);
 
         mFakeDeviceConfigInterface = new FakeDeviceConfigInterface();
         setDeviceConfigPinnedAnonSize(0);
@@ -138,10 +149,9 @@
             }
 
             @Override
-            protected PinnerService.PinnedFile pinFileInternal(String fileToPin,
-                    int maxBytesToPin, boolean attemptPinIntrospection) {
-                return new PinnerService.PinnedFile(-1,
-                        maxBytesToPin, fileToPin, maxBytesToPin);
+            protected PinnedFile pinFileInternal(PinnerService service, String fileToPin,
+                    long maxBytesToPin, boolean attemptPinIntrospection) {
+                return new PinnedFile(-1, maxBytesToPin, fileToPin, maxBytesToPin);
             }
         };
     }
@@ -167,6 +177,12 @@
         unpinAnonRegionMethod.invoke(pinnerService);
     }
 
+    private long getGlobalPinQuota(PinnerService service) throws Exception {
+        Method getQuotaMethod = PinnerService.class.getDeclaredMethod("getAvailableGlobalQuota");
+        getQuotaMethod.setAccessible(true);
+        return (long) getQuotaMethod.invoke(service);
+    }
+
     private void waitForPinnerService(PinnerService pinnerService)
             throws NoSuchFieldException, IllegalAccessException {
         // There's no notification/callback when pinning finished
@@ -315,15 +331,121 @@
         PinnerService pinnerService = new PinnerService(mContext, mInjector);
         pinnerService.onStart();
 
-        pinnerService.pinFile("test_file", 4096, null, "my_group");
+        pinnerService.pinFile("test_file", 4096, null, "my_group", false);
 
-        assertThat(getPinnedSize(pinnerService)).isGreaterThan(0);
-        assertThat(getTotalPinnedFiles(pinnerService)).isGreaterThan(0);
+        assertThat(getPinnedSize(pinnerService)).isEqualTo(4096);
+        assertThat(getTotalPinnedFiles(pinnerService)).isEqualTo(1);
 
         unpinAll(pinnerService);
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_PIN_GLOBAL_QUOTA)
+    public void testPinAllQuota() throws Exception {
+        PinnerService pinnerService = new PinnerService(mContext, mInjector);
+        pinnerService.onStart();
+
+        long quota = getGlobalPinQuota(pinnerService);
+
+        pinnerService.pinFile("test_file", Long.MAX_VALUE, null, "my_group", false);
+
+        assertThat(getPinnedSize(pinnerService)).isEqualTo(quota);
+
+        unpinAll(pinnerService);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_PIN_GLOBAL_QUOTA)
+    public void testGlobalPinQuotaAsDevicePercentage() throws Exception {
+        PinnerService pinnerService = new PinnerService(mContext, mInjector);
+        pinnerService.onStart();
+        long origQuota = getGlobalPinQuota(pinnerService);
+
+        long totalMem = android.os.Process.getTotalMemory();
+
+        // Verify that pin quota is the set percentage of device total memory
+        assertThat(origQuota).isEqualTo((totalMem * MEMORY_PERCENTAGE_FOR_QUOTA) / 100);
+
+        pinnerService.pinFile("test_file", 4096, null, "my_group", false);
+        assertThat(getGlobalPinQuota(pinnerService)).isEqualTo(origQuota - 4096);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_PIN_GLOBAL_QUOTA)
+    public void testGlobalPinWhenNoQuota() throws Exception {
+        TestableResources resources = mContext.getOrCreateTestableResources();
+        resources.addOverride(
+                com.android.internal.R.integer.config_pinnerMaxPinnedMemoryPercentage, 0);
+
+        PinnerService pinnerService = new PinnerService(mContext, mInjector);
+        pinnerService.onStart();
+
+        // Verify that pin quota is zero
+        assertThat(getGlobalPinQuota(pinnerService)).isEqualTo(0);
+
+        pinnerService.pinFile("test_file", 4096, null, "my_group", false);
+        assertThat(getTotalPinnedFiles(pinnerService)).isEqualTo(0);
+    }
+
+    /**
+     * This test is temporary, it should be cleaned up when removing the pin_global_quota bugfix
+     * flag.
+     */
+    @Test
+    @DisableFlags(Flags.FLAG_PIN_GLOBAL_QUOTA)
+    public void testGlobalQuotaDisabled() throws Exception {
+        TestableResources resources = mContext.getOrCreateTestableResources();
+        resources.addOverride(
+                com.android.internal.R.integer.config_pinnerMaxPinnedMemoryPercentage, 0);
+
+        PinnerService pinnerService = new PinnerService(mContext, mInjector);
+        pinnerService.onStart();
+
+        // The quota parameter exists but it should have no effect on pinning
+        long quota = getGlobalPinQuota(pinnerService);
+
+        pinnerService.pinFile("test_file", quota + 1, null, "my_group", false);
+
+        // Verify that we can pin past the quota as it is disabled
+        assertThat(getPinnedSize(pinnerService)).isEqualTo(quota + 1);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_PIN_GLOBAL_QUOTA)
+    public void testUnpinReleasesQuota() throws Exception {
+        PinnerService pinnerService = new PinnerService(mContext, mInjector);
+        pinnerService.onStart();
+        long origQuota = getGlobalPinQuota(pinnerService);
+
+        // Verify that pin quota exists and is non zero.
+        assertThat(getGlobalPinQuota(pinnerService)).isGreaterThan(0);
+
+        pinnerService.pinFile("test_file", origQuota, null, "my_group", false);
+
+        // Make sure all the quota was consumed
+        assertThat(getPinnedSize(pinnerService)).isEqualTo(origQuota);
+
+        // Unpin the file and verify that the quota has been released.
+        pinnerService.unpinFile("test_file");
+        assertThat(getPinnedSize(pinnerService)).isEqualTo(0);
+        assertThat(getGlobalPinQuota(pinnerService)).isEqualTo(origQuota);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_PIN_GLOBAL_QUOTA)
+    public void testGlobalPinQuotaNegative() throws Exception {
+        TestableResources resources = mContext.getOrCreateTestableResources();
+        resources.addOverride(
+                com.android.internal.R.integer.config_pinnerMaxPinnedMemoryPercentage, -10);
+
+        PinnerService pinnerService = new PinnerService(mContext, mInjector);
+        pinnerService.onStart();
+
+        // Verify that pin quota is zero
+        assertThat(getGlobalPinQuota(pinnerService)).isEqualTo(0);
+    }
+
+    @Test
     public void testPinAnonRegion() throws Exception {
         setDeviceConfigPinnedAnonSize(32768);
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 6e6d5a8..8dfd54f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -174,8 +174,8 @@
     @Mock private AccessibilityTrace mMockA11yTrace;
     @Mock private WindowManagerInternal mMockWindowManagerInternal;
     @Mock private SystemActionPerformer mMockSystemActionPerformer;
-    @Mock private IBinder mMockService;
-    @Mock private IAccessibilityServiceClient mMockServiceInterface;
+    @Mock private IBinder mMockClientBinder;
+    @Mock private IAccessibilityServiceClient mMockClient;
     @Mock private KeyEventDispatcher mMockKeyEventDispatcher;
     @Mock private IAccessibilityInteractionConnection mMockIA11yInteractionConnection;
     @Mock private IAccessibilityInteractionConnectionCallback mMockCallback;
@@ -247,9 +247,9 @@
                 mSpyServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy,
                 mMockSystemSupport, mMockA11yTrace, mMockWindowManagerInternal,
                 mMockSystemActionPerformer, mMockA11yWindowManager);
-        // Assume that the service is connected
-        mServiceConnection.mService = mMockService;
-        mServiceConnection.mServiceInterface = mMockServiceInterface;
+        // Assume that the client is connected
+        mServiceConnection.mClientBinder = mMockClientBinder;
+        mServiceConnection.mClient = mMockClient;
 
         // Update security policy for this service
         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
@@ -273,7 +273,7 @@
         final KeyEvent mockKeyEvent = mock(KeyEvent.class);
 
         mServiceConnection.onKeyEvent(mockKeyEvent, sequenceNumber);
-        verify(mMockServiceInterface).onKeyEvent(mockKeyEvent, sequenceNumber);
+        verify(mMockClient).onKeyEvent(mockKeyEvent, sequenceNumber);
     }
 
     @Test
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 566feb7..7481fc8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -63,8 +63,11 @@
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
+import android.annotation.NonNull;
 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;
@@ -212,6 +215,7 @@
     @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
     @Mock private ProxyManager mProxyManager;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private DevicePolicyManager mDevicePolicyManager;
     @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback;
     @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
     private IAccessibilityManager mA11yManagerServiceOnDevice;
@@ -241,6 +245,7 @@
                 UserManagerInternal.class, mMockUserManagerInternal);
         LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
         mInputFilter = mock(FakeInputFilter.class);
+        mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
 
         when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
                 mMockMagnificationConnectionManager);
@@ -2160,6 +2165,24 @@
                 .isEqualTo(SOFTWARE);
     }
 
+    @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);
+    }
+
+
     private Set<String> readStringsFromSetting(String setting) {
         final Set<String> result = new ArraySet<>();
         mA11yms.readColonDelimitedSettingToSet(
@@ -2280,6 +2303,7 @@
 
         private final Context mMockContext;
         private final Map<String, List<BroadcastReceiver>> mBroadcastReceivers = new ArrayMap<>();
+        private ArrayMap<Integer, Context> mMockUserContexts = new ArrayMap<>();
 
         A11yTestableContext(Context base) {
             super(base);
@@ -2317,6 +2341,19 @@
             return mMockContext;
         }
 
+        public void addMockUserContext(int userId, Context context) {
+            mMockUserContexts.put(userId, context);
+        }
+
+        @Override
+        @NonNull
+        public Context createContextAsUser(UserHandle user, int flags) {
+            if (mMockUserContexts.containsKey(user.getIdentifier())) {
+                return mMockUserContexts.get(user.getIdentifier());
+            }
+            return super.createContextAsUser(user, flags);
+        }
+
         Map<String, List<BroadcastReceiver>> getBroadcastReceivers() {
             return mBroadcastReceivers;
         }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
index 019ccf9..c76392b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
@@ -61,7 +61,6 @@
     companion object {
         const val DISPLAY_ID = 1
         const val DEVICE_ID = 123
-        const val MOUSE_POINTER_MOVEMENT_STEP = 1.8f
         // This delay is required for key events to be sent and handled correctly.
         // The handler only performs a move/scroll event if it receives the key event
         // at INTERVAL_MILLIS (which happens in practice). Hence, we need this delay in the tests.
@@ -159,8 +158,8 @@
         testLooper.dispatchAll()
 
         // Verify the sendRelativeEvent method is called once and capture the arguments
-        verifyRelativeEvents(arrayOf(-MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)),
-            arrayOf(MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)))
+        verifyRelativeEvents(arrayOf(-MouseKeysInterceptor.MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)),
+            arrayOf(MouseKeysInterceptor.MOUSE_POINTER_MOVEMENT_STEP / sqrt(2.0f)))
     }
 
     @Test
@@ -232,7 +231,8 @@
         testLooper.dispatchAll()
 
         // Verify the sendScrollEvent method is called once and capture the arguments
-        verifyScrollEvents(arrayOf<Float>(0f), arrayOf<Float>(1.0f))
+        verifyScrollEvents(arrayOf<Float>(0f),
+	    arrayOf<Float>(MouseKeysInterceptor.MOUSE_SCROLL_STEP))
     }
 
     @Test
@@ -247,7 +247,8 @@
         testLooper.dispatchAll()
 
         // Verify the sendRelativeEvent method is called once and capture the arguments
-        verifyRelativeEvents(arrayOf<Float>(0f), arrayOf<Float>(-MOUSE_POINTER_MOVEMENT_STEP))
+        verifyRelativeEvents(arrayOf<Float>(0f),
+	    arrayOf<Float>(-MouseKeysInterceptor.MOUSE_POINTER_MOVEMENT_STEP))
     }
 
     private fun verifyRelativeEvents(expectedX: Array<Float>, expectedY: Array<Float>) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 598d3a3..b745e6a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -32,6 +32,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -64,6 +65,7 @@
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -105,6 +107,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.IntConsumer;
 
@@ -700,6 +703,15 @@
     }
 
     @Test
+    public void testIntervalsOf_sendMotionEventInfo_returnMatchIntervals() {
+        FullScreenMagnificationGestureHandler.MotionEventInfo upEventQueue =
+                createEventQueue(ACTION_UP, 0, 100, 300);
+
+        List<Long> upIntervals = mMgh.mDetectingState.intervalsOf(upEventQueue, ACTION_UP);
+        assertEquals(Arrays.asList(100L, 200L), upIntervals);
+    }
+
+    @Test
     public void testMagnifierDeactivates_shortcutTriggeredState_returnToIdleState() {
         goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
 
@@ -2294,6 +2306,31 @@
         return event;
     }
 
+    private FullScreenMagnificationGestureHandler.MotionEventInfo createEventQueue(
+            int eventType, long... delays) {
+        FullScreenMagnificationGestureHandler.MotionEventInfo eventQueue = null;
+        long currentTime = SystemClock.uptimeMillis();
+
+        for (int i = 0; i < delays.length; i++) {
+            MotionEvent event = MotionEvent.obtain(currentTime + delays[i],
+                    currentTime + delays[i], eventType, 0, 0, 0);
+
+            FullScreenMagnificationGestureHandler.MotionEventInfo info =
+                    FullScreenMagnificationGestureHandler.MotionEventInfo
+                    .obtain(event, MotionEvent.obtain(event), 0);
+
+            if (eventQueue == null) {
+                eventQueue = info;
+            } else {
+                FullScreenMagnificationGestureHandler.MotionEventInfo tail = eventQueue;
+                while (tail.getNext() != null) {
+                    tail = tail.getNext();
+                }
+                tail.setNext(info);
+            }
+        }
+        return eventQueue;
+    }
 
     private String stateDump() {
         return "\nCurrent state dump:\n" + mMgh + "\n" + mHandler.getPendingMessages();
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index b9ce8ad..0c92abc 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -1163,6 +1163,16 @@
 
         verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
         Bundle result = mBundleCaptor.getValue();
+        Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+        assertNotNull(sessionBundle);
+        // Assert that session bundle is decrypted and hence data is visible.
+        assertEquals(AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1,
+                sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
+        // Assert finishSessionAsUser added calling uid and pid into the sessionBundle
+        assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_UID));
+        assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_PID));
+        assertEquals(sessionBundle.getString(
+                AccountManager.KEY_ANDROID_PACKAGE_NAME), "APCT.package");
 
         // Verify response data
         assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null));
@@ -2111,6 +2121,12 @@
                 result.getString(AccountManager.KEY_ACCOUNT_NAME));
         assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
                 result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+
+        Bundle optionBundle = result.getParcelable(
+                AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE);
+        // Assert addAccountAsUser added calling uid and pid into the option bundle
+        assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_UID));
+        assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_PID));
     }
 
     @SmallTest
@@ -3441,52 +3457,6 @@
                 + (readTotalTime.doubleValue() / readerCount / loopSize));
     }
 
-    @SmallTest
-    public void testSanitizeBundle_expectedFields() throws Exception {
-        Bundle bundle = new Bundle();
-        bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name");
-        bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, "type");
-        bundle.putString(AccountManager.KEY_AUTHTOKEN, "token");
-        bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, "label");
-        bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error message");
-        bundle.putString(AccountManager.KEY_PASSWORD, "password");
-        bundle.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN, "status");
-
-        bundle.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 123L);
-        bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
-        bundle.putInt(AccountManager.KEY_ERROR_CODE, 456);
-
-        Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle);
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name");
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE), "type");
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTHTOKEN), "token");
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTH_TOKEN_LABEL), "label");
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ERROR_MESSAGE), "error message");
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_PASSWORD), "password");
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN), "status");
-
-        assertEquals(sanitizedBundle.getLong(
-                AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0), 123L);
-        assertEquals(sanitizedBundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false), true);
-        assertEquals(sanitizedBundle.getInt(AccountManager.KEY_ERROR_CODE, 0), 456);
-    }
-
-    @SmallTest
-    public void testSanitizeBundle_filtersUnexpectedFields() throws Exception {
-        Bundle bundle = new Bundle();
-        bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name");
-        bundle.putString("unknown_key", "value");
-        Bundle sessionBundle = new Bundle();
-        bundle.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
-
-        Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle);
-
-        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name");
-        assertFalse(sanitizedBundle.containsKey("unknown_key"));
-        // It is a valid response from Authenticator which will be accessed using original Bundle
-        assertFalse(sanitizedBundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE));
-    }
-
     private void waitForCyclicBarrier(CyclicBarrier cyclicBarrier) {
         try {
             cyclicBarrier.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 4645156..3e2949d6 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -16,7 +16,9 @@
 
 package com.android.server.audio;
 
+import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX;
 import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+import static com.android.media.audio.Flags.absVolumeIndexFix;
 
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
@@ -116,7 +118,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+    @RequiresFlagsDisabled({FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME, FLAG_ABS_VOLUME_INDEX_FIX})
     public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
         AudioManager am = mContext.getSystemService(AudioManager.class);
         final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
@@ -177,6 +179,7 @@
         final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
                 /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "bla");
         final int maxPreScaleIndex = 3;
+        int passedIndex = maxIndex;
 
         for (int i = 0; i < maxPreScaleIndex; i++) {
             final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
@@ -185,9 +188,12 @@
             mAudioService.setDeviceVolume(volCur, bleDevice, mPackageName);
             mTestLooper.dispatchAll();
 
+            if (absVolumeIndexFix()) {
+                passedIndex = i + 1;
+            }
             // Stream volume changes
             verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                            AudioManager.STREAM_MUSIC, maxIndex,
+                            AudioManager.STREAM_MUSIC, passedIndex,
                             AudioSystem.DEVICE_OUT_BLE_HEADSET);
         }
 
@@ -197,8 +203,11 @@
         mAudioService.setDeviceVolume(volIndex4, bleDevice, mPackageName);
         mTestLooper.dispatchAll();
 
+        if (absVolumeIndexFix()) {
+            passedIndex = 4;
+        }
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                        AudioManager.STREAM_MUSIC, maxIndex,
+                        AudioManager.STREAM_MUSIC, passedIndex,
                         AudioSystem.DEVICE_OUT_BLE_HEADSET);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/audio/MediaFocusControlTest.java b/services/tests/servicestests/src/com/android/server/audio/MediaFocusControlTest.java
new file mode 100644
index 0000000..34878c8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/MediaFocusControlTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+package com.android.server.audio;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioFocusInfo;
+import android.media.AudioManager;
+import android.os.Binder;
+import android.os.IBinder;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class MediaFocusControlTest {
+    private static final String TAG = "MediaFocusControlTest";
+
+    private Context mContext;
+    private MediaFocusControl mMediaFocusControl;
+    private final IBinder mICallBack = new Binder();
+
+
+    private static class NoopPlayerFocusEnforcer implements PlayerFocusEnforcer {
+        public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser,
+                boolean forceDuck) {
+            return true;
+        }
+
+        public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
+        }
+
+        public void mutePlayersForCall(int[] usagesToMute) {
+        }
+
+        public void unmutePlayersForCall() {
+        }
+
+        public boolean fadeOutPlayers(@NonNull FocusRequester winner,
+                @NonNull FocusRequester loser) {
+            return true;
+        }
+
+        public void forgetUid(int uid) {
+        }
+
+        public long getFadeOutDurationMillis(@NonNull AudioAttributes aa) {
+            return 100;
+        }
+
+        public long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
+            return 100;
+        }
+
+        public boolean shouldEnforceFade() {
+            return false;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mMediaFocusControl = new MediaFocusControl(mContext, new NoopPlayerFocusEnforcer());
+    }
+
+    private static final AudioAttributes MEDIA_ATTRIBUTES = new AudioAttributes.Builder()
+            .setUsage(AudioAttributes.USAGE_MEDIA).build();
+    private static final AudioAttributes ALARM_ATTRIBUTES = new AudioAttributes.Builder()
+            .setUsage(AudioAttributes.USAGE_ALARM).build();
+    private static final int MEDIA_UID = 10300;
+    private static final int ALARM_UID = 10301;
+
+    /**
+     * Test {@link MediaFocusControl#sendFocusLossAndUpdate(AudioFocusInfo)}
+     */
+    @Test
+    public void testSendFocusLossAndUpdate() throws Exception {
+        // simulate a media app requesting focus, followed by an alarm
+        mMediaFocusControl.requestAudioFocus(MEDIA_ATTRIBUTES, AudioManager.AUDIOFOCUS_GAIN,
+                mICallBack, null /*focusDispatcher*/, "clientMedia", "packMedia",
+                AudioManager.AUDIOFOCUS_FLAG_TEST /*flags*/, 35 /*sdk*/, false/*forceDuck*/,
+                MEDIA_UID, true /*permissionOverridesCheck*/);
+        final AudioFocusInfo alarm = new AudioFocusInfo(ALARM_ATTRIBUTES, ALARM_UID,
+                "clientAlarm", "packAlarm",
+                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0/*lossReceived*/,
+                AudioManager.AUDIOFOCUS_FLAG_TEST /*flags*/, 35 /*sdk*/);
+        mMediaFocusControl.requestAudioFocus(alarm.getAttributes(), alarm.getGainRequest(),
+                mICallBack, null /*focusDispatcher*/, alarm.getClientId(), alarm.getPackageName(),
+                alarm.getFlags(), alarm.getSdkTarget(), false/*forceDuck*/,
+                alarm.getClientUid(), true /*permissionOverridesCheck*/);
+        // verify stack is in expected state
+        List<AudioFocusInfo> stack = mMediaFocusControl.getFocusStack();
+        Assert.assertEquals("focus stack should have 2 entries", 2, stack.size());
+        Assert.assertEquals("focus loser should have received LOSS_TRANSIENT_CAN_DUCK",
+                AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK, stack.get(0).getLossReceived());
+
+        // make alarm app lose focus and check stack
+        mMediaFocusControl.sendFocusLossAndUpdate(alarm);
+        stack = mMediaFocusControl.getFocusStack();
+        Assert.assertEquals("focus stack should have 1 entry after sendFocusLossAndUpdate",
+                1, stack.size());
+        Assert.assertEquals("new top of stack should be media app",
+                MEDIA_UID, stack.get(0).getClientUid());
+    }
+}
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 beed0a3d..c305fd9 100644
--- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -39,8 +39,9 @@
 import static android.view.KeyEvent.ACTION_DOWN;
 import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
 
-import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
 import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX;
+import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+import static com.android.media.audio.Flags.absVolumeIndexFix;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -627,7 +628,6 @@
 
     @Test
     @RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
-    @RequiresFlagsDisabled(FLAG_ABS_VOLUME_INDEX_FIX)
     public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
         final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
         final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
@@ -638,6 +638,7 @@
         final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
                 /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "bla");
         final int maxPreScaleIndex = 3;
+        int passedIndex = maxIndex;
 
         for (int i = 0; i < maxPreScaleIndex; i++) {
             final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
@@ -646,9 +647,12 @@
             mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName());
             mTestLooper.dispatchAll();
 
+            if (absVolumeIndexFix()) {
+                passedIndex = i + 1;
+            }
             // Stream volume changes
             verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                    STREAM_MUSIC, maxIndex,
+                    STREAM_MUSIC, passedIndex,
                     AudioSystem.DEVICE_OUT_BLE_HEADSET);
         }
 
@@ -658,8 +662,11 @@
         mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName());
         mTestLooper.dispatchAll();
 
+        if (absVolumeIndexFix()) {
+            passedIndex = 4;
+        }
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                STREAM_MUSIC, maxIndex,
+                STREAM_MUSIC, passedIndex,
                 AudioSystem.DEVICE_OUT_BLE_HEADSET);
     }
 
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 abc9ce3..ee63d5d 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
@@ -38,6 +38,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -91,6 +92,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
@@ -174,8 +176,8 @@
     private PackageManager mPackageManager;
     @Mock
     private KeyguardManager mKeyguardManager;
-    @Mock
-    AppOpsManager mAppOpsManager;
+
+    private AppOpsManager mAppOpsManager;
     @Mock
     private IMediaProjectionWatcherCallback mWatcherCallback;
     @Mock
@@ -193,6 +195,7 @@
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
 
+        mAppOpsManager = mockAppOpsManager();
         mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
         mContext.addMockSystemService(KeyguardManager.class, mKeyguardManager);
         mContext.setMockPackageManager(mPackageManager);
@@ -206,6 +209,17 @@
         mService = new MediaProjectionManagerService(mContext);
     }
 
+    private static AppOpsManager mockAppOpsManager() {
+        return mock(AppOpsManager.class, invocationOnMock -> {
+            if (invocationOnMock.getMethod().getName().startsWith("noteOp")) {
+                // Mockito will return 0 for non-stubbed method which corresponds to MODE_ALLOWED
+                // and is not what we want.
+                return AppOpsManager.MODE_IGNORED;
+            }
+            return Answers.RETURNS_DEFAULTS.answer(invocationOnMock);
+        });
+    }
+
     @After
     public void tearDown() {
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
@@ -305,8 +319,10 @@
     public void testCreateProjection_keyguardLocked_AppOpMediaProjection()
             throws NameNotFoundException {
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
-        doReturn(true).when(mAppOpsManager).isOperationActive(eq(AppOpsManager.OP_PROJECT_MEDIA),
-                eq(projection.uid), eq(projection.packageName));
+        doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager)
+                .noteOpNoThrow(eq(AppOpsManager.OP_PROJECT_MEDIA),
+                        eq(projection.uid), eq(projection.packageName), nullable(String.class),
+                        nullable(String.class));
         doReturn(true).when(mKeyguardManager).isKeyguardLocked();
 
         doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
@@ -1159,7 +1175,7 @@
         doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
                 any(ApplicationInfoFlags.class), any(UserHandle.class));
         return service.createProjectionInternal(UID, PACKAGE_NAME,
-                TYPE_MIRRORING, /* isPermanentGrant= */ true, UserHandle.CURRENT);
+                TYPE_MIRRORING, /* isPermanentGrant= */ false, UserHandle.CURRENT);
     }
 
     // Set up preconditions for starting a projection, with no foreground service requirements.
diff --git a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
index f91f77a..cdfc521 100644
--- a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
@@ -86,7 +86,6 @@
     private File mDataBlockFile;
     private File mFrpSecretFile;
     private File mFrpSecretTmpFile;
-    private String mOemUnlockPropertyValue;
     private boolean mIsUpgradingFromPreV = false;
 
     @Mock private UserManager mUserManager;
@@ -105,13 +104,6 @@
         }
 
         @Override
-        void setProperty(String key, String value) {
-            // Override to capture the value instead of actually setting the property.
-            assertThat(key).isEqualTo("sys.oem_unlock_allowed");
-            mOemUnlockPropertyValue = value;
-        }
-
-        @Override
         boolean isUpgradingFromPreVRelease() {
             return mIsUpgradingFromPreV;
         }
@@ -598,7 +590,6 @@
 
         mInterface.setOemUnlockEnabled(true);
         assertThat(mInterface.getOemUnlockEnabled()).isTrue();
-        assertThat(mOemUnlockPropertyValue).isEqualTo("1");
     }
 
     @Test
@@ -635,7 +626,6 @@
         // The current implementation does not check digest before set or get the oem unlock bit.
         tamperWithDigest();
         mInterface.setOemUnlockEnabled(true);
-        assertThat(mOemUnlockPropertyValue).isEqualTo("1");
         tamperWithDigest();
         assertThat(mInterface.getOemUnlockEnabled()).isTrue();
     }
@@ -676,7 +666,6 @@
 
         mInternalInterface.forceOemUnlockEnabled(true);
 
-        assertThat(mOemUnlockPropertyValue).isEqualTo("1");
         assertThat(readBackingFile(mPdbService.getOemUnlockDataOffset(), 1).array())
                 .isEqualTo(new byte[] { 1 });
     }
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
new file mode 100644
index 0000000..6bd4279
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.supervision
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.server.LocalServices
+import com.android.server.pm.UserManagerInternal
+import com.google.common.truth.Truth.assertThat
+import androidx.test.platform.app.InstrumentationRegistry
+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
+
+/**
+ * Unit tests for {@link SupervisionService}.
+ * <p/>
+ * Run with <code>atest SupervisionServiceTest</code>.
+ */
+@RunWith(AndroidJUnit4::class)
+class SupervisionServiceTest {
+    companion object {
+        const val USER_ID = 100
+    }
+
+    private lateinit var service: SupervisionService
+
+    @Rule
+    @JvmField
+    val mocks: MockitoRule = MockitoJUnit.rule()
+
+    @Mock
+    private lateinit var mockUserManagerInternal: UserManagerInternal
+
+    @Before
+    fun setup() {
+        val context = InstrumentationRegistry.getInstrumentation().context
+
+        LocalServices.removeServiceForTest(UserManagerInternal::class.java)
+        LocalServices.addService(UserManagerInternal::class.java, mockUserManagerInternal)
+
+        service = SupervisionService(context)
+    }
+
+    @Test
+    fun testSetSupervisionEnabledForUser() {
+        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+
+        service.setSupervisionEnabledForUser(USER_ID, true)
+        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+
+        service.setSupervisionEnabledForUser(USER_ID, false)
+        assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+    }
+
+    @Test
+    fun testSetSupervisionLockscreenEnabledForUser() {
+        var userData = service.getUserDataLocked(USER_ID)
+        assertThat(userData.supervisionLockScreenEnabled).isFalse()
+        assertThat(userData.supervisionLockScreenOptions).isNull()
+
+        service.mInternal.setSupervisionLockscreenEnabledForUser(USER_ID, true, Bundle())
+        userData = service.getUserDataLocked(USER_ID)
+        assertThat(userData.supervisionLockScreenEnabled).isTrue()
+        assertThat(userData.supervisionLockScreenOptions).isNotNull()
+
+        service.mInternal.setSupervisionLockscreenEnabledForUser(USER_ID, false, null)
+        userData = service.getUserDataLocked(USER_ID)
+        assertThat(userData.supervisionLockScreenEnabled).isFalse()
+        assertThat(userData.supervisionLockScreenOptions).isNull()
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
index b09e9b1..54282ff 100644
--- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
@@ -119,7 +119,7 @@
      */
     private class TestInjector extends AnrTimer.Injector {
         @Override
-        boolean anrTimerServiceEnabled() {
+        boolean serviceEnabled() {
             return mEnabled;
         }
     }
diff --git a/services/tests/timetests/Android.bp b/services/tests/timetests/Android.bp
index aae6acc..65a694e 100644
--- a/services/tests/timetests/Android.bp
+++ b/services/tests/timetests/Android.bp
@@ -19,6 +19,7 @@
         "platform-test-annotations",
         "services.core",
         "truth",
+        "ApplicationSharedMemoryTestRule",
     ],
     libs: ["android.test.runner.stubs.system"],
     platform_apis: true,
diff --git a/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index c64ec72..3836063 100644
--- a/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -46,6 +46,7 @@
 import android.os.TimestampedValue;
 import android.util.IndentingPrintWriter;
 
+import com.android.internal.os.ApplicationSharedMemoryTestRule;
 import com.android.server.SystemClockTime.TimeConfidence;
 import com.android.server.timedetector.TimeDetectorStrategy.Origin;
 import com.android.server.timezonedetector.StateChangeListener;
@@ -55,6 +56,7 @@
 import junitparams.Parameters;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -125,6 +127,10 @@
                     .setAutoDetectionEnabledSetting(true)
                     .build();
 
+    @Rule
+    public final ApplicationSharedMemoryTestRule mApplicationSharedMemoryTestRule =
+            new ApplicationSharedMemoryTestRule();
+
     private FakeEnvironment mFakeEnvironment;
     private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy;
     private TimeDetectorStrategyImpl mTimeDetectorStrategy;
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 b8f9767..44770d2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,6 +38,7 @@
 import static android.app.Notification.FLAG_NO_DISMISS;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
+import static android.app.Notification.FLAG_PROMOTED_ONGOING;
 import static android.app.Notification.FLAG_USER_INITIATED_JOB;
 import static android.app.Notification.GROUP_ALERT_CHILDREN;
 import static android.app.Notification.VISIBILITY_PRIVATE;
@@ -53,6 +54,7 @@
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MAX;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
@@ -468,6 +470,8 @@
 
     NotificationChannel mSilentChannel = new NotificationChannel("low", "low", IMPORTANCE_LOW);
 
+    NotificationChannel mMinChannel = new NotificationChannel("min", "min", IMPORTANCE_MIN);
+
     private static final int NOTIFICATION_LOCATION_UNKNOWN = 0;
 
     private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
@@ -558,8 +562,7 @@
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.allCombinationsOf(
-                FLAG_ALL_NOTIFS_NEED_TTL);
+        return FlagsParameterization.allCombinationsOf();
     }
 
     public NotificationManagerServiceTest(FlagsParameterization flags) {
@@ -856,15 +859,17 @@
         mInternalService = mService.getInternalService();
 
         mBinderService.createNotificationChannels(mPkg, new ParceledListSlice(
-                Arrays.asList(mTestNotificationChannel, mSilentChannel)));
+                Arrays.asList(mTestNotificationChannel, mSilentChannel, mMinChannel)));
         mBinderService.createNotificationChannels(PKG_P, new ParceledListSlice(
-                Arrays.asList(mTestNotificationChannel, mSilentChannel)));
+                Arrays.asList(mTestNotificationChannel, mSilentChannel, mMinChannel)));
         mBinderService.createNotificationChannels(PKG_O, new ParceledListSlice(
-                Arrays.asList(mTestNotificationChannel, mSilentChannel)));
+                Arrays.asList(mTestNotificationChannel, mSilentChannel, mMinChannel)));
         assertNotNull(mBinderService.getNotificationChannel(
                 mPkg, mContext.getUserId(), mPkg, TEST_CHANNEL_ID));
         assertNotNull(mBinderService.getNotificationChannel(
                 mPkg, mContext.getUserId(), mPkg, mSilentChannel.getId()));
+        assertNotNull(mBinderService.getNotificationChannel(
+                mPkg, mContext.getUserId(), mPkg, mMinChannel.getId()));
         clearInvocations(mRankingHandler);
         when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
 
@@ -943,6 +948,16 @@
         }
     }
 
+    private ShortcutInfo createMockConvoShortcut() {
+        ShortcutInfo info = mock(ShortcutInfo.class);
+        when(info.getPackage()).thenReturn(mPkg);
+        when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
+        when(info.getUserId()).thenReturn(USER_SYSTEM);
+        when(info.isLongLived()).thenReturn(true);
+        when(info.isEnabled()).thenReturn(true);
+        return info;
+    }
+
     private void simulatePackageSuspendBroadcast(boolean suspend, String pkg,
             int uid) {
         // mimics receive broadcast that package is (un)suspended
@@ -4637,7 +4652,42 @@
 
         doThrow(new SecurityException("no access")).when(mUgmInternal)
                 .checkGrantUriPermission(eq(Process.myUid()), any(), eq(soundUri),
-                    anyInt(), eq(Process.myUserHandle().getIdentifier()));
+                anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+        mBinderService.updateNotificationChannelFromPrivilegedListener(
+                null, mPkg, Process.myUserHandle(), updatedNotificationChannel);
+
+        verify(mPreferencesHelper, times(1)).updateNotificationChannel(
+                anyString(), anyInt(), any(), anyBoolean(),  anyInt(), anyBoolean());
+
+        verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+                eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+                eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+    }
+
+    @Test
+    public void
+        testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
+            throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(singletonList(mock(AssociationInfo.class)));
+        when(mPreferencesHelper.getNotificationChannel(eq(mPkg), anyInt(),
+                eq(mTestNotificationChannel.getId()), anyBoolean()))
+                .thenReturn(mTestNotificationChannel);
+
+        // Missing Uri permissions for the old channel sound
+        final Uri oldSoundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+        doThrow(new SecurityException("no access")).when(mUgmInternal)
+                .checkGrantUriPermission(eq(Process.myUid()), any(), eq(oldSoundUri),
+                anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+        // Has Uri permissions for the old channel sound
+        final Uri newSoundUri = Uri.parse("content://media/test/sound/uri");
+        final NotificationChannel updatedNotificationChannel = new NotificationChannel(
+                TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+        updatedNotificationChannel.setSound(newSoundUri,
+                updatedNotificationChannel.getAudioAttributes());
 
         mBinderService.updateNotificationChannelFromPrivilegedListener(
                 null, mPkg, Process.myUserHandle(), updatedNotificationChannel);
@@ -16540,13 +16590,297 @@
         assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
     }
 
-    private ShortcutInfo createMockConvoShortcut() {
-        ShortcutInfo info = mock(ShortcutInfo.class);
-        when(info.getPackage()).thenReturn(mPkg);
-        when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
-        when(info.getUserId()).thenReturn(USER_SYSTEM);
-        when(info.isLongLived()).thenReturn(true);
-        when(info.isEnabled()).thenReturn(true);
-        return info;
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testSetCanBePromoted_granted() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
+        // qualifying posted notification
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+                .build();
+
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        // qualifying enqueued notification
+        Notification n1 = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+                .build();
+        StatusBarNotification sbn1 = new StatusBarNotification(mPkg, mPkg, 7, null, mUid, 0,
+                n1, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r1 = new NotificationRecord(mContext, sbn1, mTestNotificationChannel);
+
+        // another package but otherwise would qualify
+        Notification n2 = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+                .build();
+        StatusBarNotification sbn2 = new StatusBarNotification(PKG_O, PKG_O, 7, null, UID_O, 0,
+                n2, UserHandle.getUserHandleForUid(UID_O), null, 0);
+        NotificationRecord r2 = new NotificationRecord(mContext, sbn2, mTestNotificationChannel);
+
+        // not-qualifying posted notification
+        Notification n3 = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .build();
+
+        StatusBarNotification sbn3 = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+                n3, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r3 = new NotificationRecord(mContext, sbn3, mTestNotificationChannel);
+
+        mService.addNotification(r3);
+        mService.addNotification(r2);
+        mService.addNotification(r);
+        mService.addEnqueuedNotification(r1);
+
+        mBinderService.setCanBePromoted(mPkg, mUid, true, true);
+
+        waitForIdle();
+
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(
+                captor.capture(), any(), anyBoolean());
+
+        // the posted one
+        assertThat(mService.hasFlag(captor.getValue().getNotification().flags,
+                FLAG_PROMOTED_ONGOING)).isTrue();
+        // the enqueued one
+        assertThat(mService.hasFlag(r1.getNotification().flags, FLAG_PROMOTED_ONGOING)).isTrue();
+        // the other app
+        assertThat(mService.hasFlag(r2.getNotification().flags, FLAG_PROMOTED_ONGOING)).isFalse();
+        // same app, not qualifying
+        assertThat(mService.hasFlag(r3.getNotification().flags, FLAG_PROMOTED_ONGOING)).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testSetCanBePromoted_granted_onlyNotifiesOnce() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
+        // qualifying posted notification
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+                .build();
+
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addNotification(r);
+
+        mBinderService.setCanBePromoted(mPkg, mUid, true, true);
+        waitForIdle();
+        mBinderService.setCanBePromoted(mPkg, mUid, true, true);
+        waitForIdle();
+
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(
+                captor.capture(), any(), anyBoolean());
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testSetCanBePromoted_revoked() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
+        // start from true state
+        mBinderService.setCanBePromoted(mPkg, mUid, true, true);
+
+        // qualifying posted notification
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
+                .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+                .build();
+
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        // qualifying enqueued notification
+        Notification n1 = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
+                .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+                .build();
+        StatusBarNotification sbn1 = new StatusBarNotification(mPkg, mPkg, 7, null, mUid, 0,
+                n1, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r1 = new NotificationRecord(mContext, sbn1, mTestNotificationChannel);
+
+        // doesn't qualify, same package
+        Notification n2 = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .build();
+        StatusBarNotification sbn2 = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+                n2, UserHandle.getUserHandleForUid(UID_O), null, 0);
+        NotificationRecord r2 = new NotificationRecord(mContext, sbn2, mTestNotificationChannel);
+
+        mService.addNotification(r2);
+        mService.addNotification(r);
+        mService.addEnqueuedNotification(r1);
+
+        mBinderService.setCanBePromoted(mPkg, mUid, false, true);
+
+        waitForIdle();
+
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(
+                captor.capture(), any(), anyBoolean());
+
+        // the posted one
+        assertThat(mService.hasFlag(captor.getValue().getNotification().flags,
+                FLAG_PROMOTED_ONGOING)).isFalse();
+        // the enqueued one
+        assertThat(mService.hasFlag(r1.getNotification().flags, FLAG_PROMOTED_ONGOING)).isFalse();
+        // the not qualifying one
+        assertThat(mService.hasFlag(r2.getNotification().flags, FLAG_PROMOTED_ONGOING)).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testSetCanBePromoted_revoked_onlyNotifiesOnce() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
+        // start from true state
+        mBinderService.setCanBePromoted(mPkg, mUid, true, true);
+
+        // qualifying posted notification
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .setFlag(FLAG_PROMOTED_ONGOING, true) // add manually since we're skipping post
+                .setFlag(FLAG_CAN_COLORIZE, true) // add manually since we're skipping post
+                .build();
+
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addNotification(r);
+
+        mBinderService.setCanBePromoted(mPkg, mUid, false, true);
+        waitForIdle();
+        mBinderService.setCanBePromoted(mPkg, mUid, false, true);
+        waitForIdle();
+
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(
+                captor.capture(), any(), anyBoolean());
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testPostPromotableNotification() throws Exception {
+        mBinderService.setCanBePromoted(mPkg, mUid, true, true);
+        assertThat(mBinderService.appCanBePromoted(mPkg, mUid)).isTrue();
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
+
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+
+        mBinderService.enqueueNotificationWithTag(mPkg, mPkg, sbn.getTag(),
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        waitForIdle();
+
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(
+                captor.capture(), any(), anyBoolean());
+
+        assertThat(mService.hasFlag(captor.getValue().getNotification().flags,
+                FLAG_PROMOTED_ONGOING)).isTrue();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testPostPromotableNotification_noPermission() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
+        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .build();
+
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+
+        mBinderService.enqueueNotificationWithTag(mPkg, mPkg, sbn.getTag(),
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        waitForIdle();
+
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(
+                captor.capture(), any(), anyBoolean());
+
+        assertThat(mService.hasFlag(captor.getValue().getNotification().flags,
+                FLAG_PROMOTED_ONGOING)).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testPostPromotableNotification_unimportantNotification() throws Exception {
+        mBinderService.setCanBePromoted(mPkg, mUid, true, true);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
+        Notification n = new Notification.Builder(mContext, mMinChannel.getId())
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
+                .setColor(Color.WHITE)
+                .setColorized(true)
+                .build();
+
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 9, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+
+        mBinderService.enqueueNotificationWithTag(mPkg, mPkg, sbn.getTag(),
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        waitForIdle();
+
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(
+                captor.capture(), any(), anyBoolean());
+
+        assertThat(mService.hasFlag(captor.getValue().getNotification().flags,
+                FLAG_PROMOTED_ONGOING)).isFalse();
     }
 }
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 1905ae4..d64b9e8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -45,11 +45,13 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE;
+import static android.content.ContentResolver.SCHEME_CONTENT;
+import static android.content.ContentResolver.SCHEME_FILE;
 import static android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_SYSTEM;
-
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
 import static android.service.notification.Flags.notificationClassification;
@@ -59,9 +61,11 @@
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
 import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
+import static com.android.server.notification.Flags.FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI;
 import static com.android.server.notification.Flags.FLAG_PERSIST_INCOMPLETE_RESTORE_DATA;
 import static com.android.server.notification.NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER;
 import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE;
+import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_PROMOTABLE;
 import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
 import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT;
 import static com.android.server.notification.PreferencesHelper.UNKNOWN_UID;
@@ -83,6 +87,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 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.reset;
@@ -368,10 +373,10 @@
 
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                false, mClock);
+                mUgmInternal, false, mClock);
         mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                false, mClock);
+                mUgmInternal, false, mClock);
         resetZenModeHelper();
 
         mAudioAttributes = new AudioAttributes.Builder()
@@ -518,6 +523,17 @@
         doneLatch.await();
     }
 
+    private static NotificationChannel cloneChannel(NotificationChannel original) {
+        Parcel parcel = Parcel.obtain();
+        try {
+            original.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            return NotificationChannel.CREATOR.createFromParcel(parcel);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
     @Test
     public void testWriteXml_onlyBackupsTargetUser() throws Exception {
         // Setup package notifications.
@@ -631,6 +647,9 @@
         }
 
         mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+        if (android.app.Flags.uiRichOngoing()) {
+            mHelper.setCanBePromoted(PKG_N_MR1, UID_N_MR1, true, true);
+        }
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
                 UserHandle.USER_ALL, channel1.getId(), channel2.getId(),
@@ -641,6 +660,11 @@
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertTrue(mXmlHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+        if (android.app.Flags.uiRichOngoing()) {
+            assertThat(mXmlHelper.canBePromoted(PKG_N_MR1, UID_N_MR1)).isTrue();
+            assertThat(mXmlHelper.getAppLockedFields(PKG_N_MR1, UID_N_MR1) & USER_LOCKED_PROMOTABLE)
+                    .isNotEqualTo(0);
+        }
         assertEquals(channel1,
                 mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
         compareChannels(channel2,
@@ -763,7 +787,7 @@
     public void testReadXml_oldXml_migrates() throws Exception {
         mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                /* showReviewPermissionsNotification= */ true, mClock);
+                mUgmInternal, /* showReviewPermissionsNotification= */ true, mClock);
 
         String xml = "<ranking version=\"2\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -899,7 +923,7 @@
     public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
         mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                /* showReviewPermissionsNotification= */ true, mClock);
+                mUgmInternal, /* showReviewPermissionsNotification= */ true, mClock);
 
         String xml = "<ranking version=\"3\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -958,7 +982,7 @@
     public void testReadXml_newXml_permissionNotificationOff() throws Exception {
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                /* showReviewPermissionsNotification= */ false, mClock);
+                mUgmInternal, /* showReviewPermissionsNotification= */ false, mClock);
 
         String xml = "<ranking version=\"3\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -1017,7 +1041,7 @@
     public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                /* showReviewPermissionsNotification= */ true, mClock);
+                mUgmInternal, /* showReviewPermissionsNotification= */ true, mClock);
 
         String xml = "<ranking version=\"4\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -1689,7 +1713,7 @@
         // simulate load after reboot
         mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                false, mClock);
+                mUgmInternal, false, mClock);
         loadByteArrayXml(baos.toByteArray(), false, USER_ALL);
 
         // Trigger 2nd restore pass
@@ -1744,7 +1768,7 @@
         // simulate load after reboot
         mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                false, mClock);
+                mUgmInternal, false, mClock);
         loadByteArrayXml(xml.getBytes(), false, USER_ALL);
 
         // Trigger 2nd restore pass
@@ -1822,10 +1846,10 @@
 
         mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                false, mClock);
+                mUgmInternal, false, mClock);
         mXmlHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
-                false, mClock);
+                mUgmInternal, false, mClock);
 
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -3029,6 +3053,64 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI)
+    public void testCreateChannel_noSoundUriPermission_contentSchemeVerified() {
+        final Uri sound = Uri.parse(SCHEME_CONTENT + "://media/test/sound/uri");
+
+        doThrow(new SecurityException("no access")).when(mUgmInternal)
+                .checkGrantUriPermission(eq(UID_N_MR1), any(), eq(sound),
+                    anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+        final NotificationChannel channel = new NotificationChannel("id2", "name2",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setSound(sound, mAudioAttributes);
+
+        assertThrows(SecurityException.class,
+                () -> mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel,
+                    true, false, UID_N_MR1, false));
+        assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true))
+                .isNull();
+    }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI)
+    public void testCreateChannel_noSoundUriPermission_fileSchemaIgnored() {
+        final Uri sound = Uri.parse(SCHEME_FILE + "://path/sound");
+
+        doThrow(new SecurityException("no access")).when(mUgmInternal)
+                .checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
+                    anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+        final NotificationChannel channel = new NotificationChannel("id2", "name2",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setSound(sound, mAudioAttributes);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+                false);
+        assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true)
+                .getSound()).isEqualTo(sound);
+    }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI)
+    public void testCreateChannel_noSoundUriPermission_resourceSchemaIgnored() {
+        final Uri sound = Uri.parse(SCHEME_ANDROID_RESOURCE + "://resId/sound");
+
+        doThrow(new SecurityException("no access")).when(mUgmInternal)
+                .checkGrantUriPermission(eq(UID_N_MR1), any(), any(),
+                    anyInt(), eq(Process.myUserHandle().getIdentifier()));
+
+        final NotificationChannel channel = new NotificationChannel("id2", "name2",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setSound(sound, mAudioAttributes);
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+                false);
+        assertThat(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true)
+                .getSound()).isEqualTo(sound);
+    }
+
+    @Test
     public void testPermanentlyDeleteChannels() throws Exception {
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -6293,14 +6375,31 @@
         }, 20, 50);
     }
 
-    private static NotificationChannel cloneChannel(NotificationChannel original) {
-        Parcel parcel = Parcel.obtain();
-        try {
-            original.writeToParcel(parcel, 0);
-            parcel.setDataPosition(0);
-            return NotificationChannel.CREATOR.createFromParcel(parcel);
-        } finally {
-            parcel.recycle();
-        }
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testNoAppHasPermissionToPromoteByDefault() {
+        mHelper.setShowBadge(PKG_P, UID_P, true);
+        assertThat(mHelper.canBePromoted(PKG_P, UID_P)).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testSetCanBePromoted() {
+        mHelper.setCanBePromoted(PKG_P, UID_P, true, true);
+        assertThat(mHelper.canBePromoted(PKG_P, UID_P)).isTrue();
+
+        mHelper.setCanBePromoted(PKG_P, UID_P, false, true);
+        assertThat(mHelper.canBePromoted(PKG_P, UID_P)).isFalse();
+        verify(mHandler, never()).requestSort();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
+    public void testSetCanBePromoted_allowlistNotOverrideUser() {
+        mHelper.setCanBePromoted(PKG_P, UID_P, true, true);
+        assertThat(mHelper.canBePromoted(PKG_P, UID_P)).isTrue();
+
+        mHelper.setCanBePromoted(PKG_P, UID_P, false, false);
+        assertThat(mHelper.canBePromoted(PKG_P, UID_P)).isTrue();
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 9b87947..d4cba8d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -18,7 +18,7 @@
 
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
-import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
 import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
 import static android.app.Flags.FLAG_MODES_API;
 import static android.app.Flags.FLAG_MODES_UI;
@@ -221,7 +221,7 @@
 @TestableLooper.RunWithLooper
 public class ZenModeHelperTest extends UiServiceTestCase {
 
-    private static final String EVENTS_DEFAULT_RULE_ID = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
+    private static final String EVENTS_DEFAULT_RULE_ID = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID;
     private static final String SCHEDULE_DEFAULT_RULE_ID =
             ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
     private static final String CUSTOM_PKG_NAME = "not.android";
@@ -1216,7 +1216,7 @@
 
         // list for tracking which ids we've seen in the pulled atom output
         List<String> ids = new ArrayList<>();
-        ids.addAll(ZenModeConfig.DEFAULT_RULE_IDS);
+        ids.addAll(ZenModeConfig.getDefaultRuleIds());
         ids.add("");  // empty string for root config
 
         for (StatsEvent ev : events) {
@@ -1793,14 +1793,13 @@
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
         assertTrue(rules.size() != 0);
-        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+        for (String defaultId : ZenModeConfig.getDefaultRuleIds()) {
             assertTrue(rules.containsKey(defaultId));
         }
 
         assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
     }
 
-
     @Test
     public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception {
         setupZenConfig();
@@ -1830,7 +1829,7 @@
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
         assertTrue(rules.size() != 0);
-        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+        for (String defaultId : ZenModeConfig.getDefaultRuleIds()) {
             assertTrue(rules.containsKey(defaultId));
         }
         assertFalse(rules.containsKey("customRule"));
@@ -1839,6 +1838,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MODES_UI) // modes_ui has only 1 default rule
     public void testReadXmlOnlyOneDefaultRuleExists() throws Exception {
         setupZenConfig();
         Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
@@ -1882,11 +1882,11 @@
 
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
-        assertTrue(rules.size() != 0);
-        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
-            assertTrue(rules.containsKey(defaultId));
+        assertThat(rules).isNotEmpty();
+        for (String defaultId : ZenModeConfig.getDefaultRuleIds()) {
+            assertThat(rules).containsKey(defaultId);
         }
-        assertFalse(rules.containsKey("customRule"));
+        assertThat(rules).doesNotContainKey("customRule");
 
         assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
     }
@@ -1932,13 +1932,13 @@
         defaultEventRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultEventRuleInfo);
-        defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
+        defaultEventRule.id = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID;
         defaultScheduleRule.zenPolicy = new ZenPolicy.Builder()
                 .allowAlarms(false)
                 .allowMedia(false)
                 .allowRepeatCallers(false)
                 .build();
-        automaticRules.put(ZenModeConfig.EVENTS_DEFAULT_RULE_ID, defaultEventRule);
+        automaticRules.put(ZenModeConfig.EVENTS_OBSOLETE_RULE_ID, defaultEventRule);
 
         mZenModeHelper.mConfig.automaticRules = automaticRules;
 
@@ -1951,18 +1951,19 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         // check default rules
+        int expectedNumAutoRules = 1 + ZenModeConfig.getDefaultRuleIds().size(); // custom + default
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
-        assertEquals(3, rules.size());
-        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
-            assertTrue(rules.containsKey(defaultId));
+        assertThat(rules).hasSize(expectedNumAutoRules);
+        for (String defaultId : ZenModeConfig.getDefaultRuleIds()) {
+            assertThat(rules).containsKey(defaultId);
         }
-        assertTrue(rules.containsKey("customRule"));
+        assertThat(rules).containsKey("customRule");
 
         assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
 
         List<StatsEvent> events = new LinkedList<>();
         mZenModeHelper.pullRules(events);
-        assertEquals(4, events.size());
+        assertThat(events).hasSize(expectedNumAutoRules + 1); // auto + manual
     }
 
     @Test
@@ -2151,8 +2152,8 @@
         defaultEventRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultEventRuleInfo);
-        defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
-        automaticRules.put(ZenModeConfig.EVENTS_DEFAULT_RULE_ID, defaultEventRule);
+        defaultEventRule.id = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID;
+        automaticRules.put(ZenModeConfig.EVENTS_OBSOLETE_RULE_ID, defaultEventRule);
 
         mZenModeHelper.mConfig.automaticRules = automaticRules;
 
@@ -2167,7 +2168,7 @@
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
         assertThat(rules.size()).isGreaterThan(0);
-        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+        for (String defaultId : ZenModeConfig.getDefaultRuleIds()) {
             assertThat(rules).containsKey(defaultId);
             ZenRule rule = rules.get(defaultId);
             assertThat(rule.zenPolicy).isNotNull();
@@ -2371,7 +2372,7 @@
         // Find default rules; check they have non-null policies; check that they match the default
         // and not whatever has been set up in setupZenConfig.
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
-        for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
+        for (String defaultId : ZenModeConfig.getDefaultRuleIds()) {
             assertThat(rules).containsKey(defaultId);
             ZenRule rule = rules.get(defaultId);
             assertThat(rule.zenPolicy).isNotNull();
@@ -6884,7 +6885,7 @@
         mZenModeHelper.onUserSwitched(101);
 
         ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
-                ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+                ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
 
         assertThat(eventsRule).isNotNull();
         assertThat(eventsRule.zenPolicy).isNull();
@@ -6900,7 +6901,7 @@
         mZenModeHelper.onUserSwitched(201);
 
         ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
-                ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+                ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
 
         assertThat(eventsRule).isNotNull();
         assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
@@ -6915,11 +6916,11 @@
         mZenModeHelper.onUserSwitched(301);
 
         ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
-                ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+                ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
 
         assertThat(eventsRule).isNotNull();
         assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
-        assertThat(eventsRule.type).isEqualTo(TYPE_SCHEDULE_CALENDAR);
+        assertThat(eventsRule.type).isEqualTo(TYPE_SCHEDULE_TIME);
         assertThat(eventsRule.triggerDescription).isNotEmpty();
     }
 
@@ -6992,6 +6993,62 @@
         assertThat(zenRule.condition).isNotNull();
     }
 
+    @Test
+    @EnableFlags(FLAG_MODES_API)
+    public void addAutomaticZenRule_withoutPolicy_getsItsOwnInstanceOfDefaultPolicy() {
+        // Add a rule without policy -> uses default config
+        AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, azr, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+
+        ZenRule zenRule = checkNotNull(mZenModeHelper.mConfig.automaticRules.get(ruleId));
+
+        assertThat(zenRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
+        assertThat(zenRule.zenPolicy).isNotSameInstanceAs(mZenModeHelper.getDefaultZenPolicy());
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void readXml_withDisabledEventsRule_deletesIt() throws Exception {
+        ZenRule rule = new ZenRule();
+        rule.id = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID;
+        rule.name = "Events";
+        rule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        rule.conditionId = Uri.parse("events");
+
+        rule.enabled = false;
+        mZenModeHelper.mConfig.automaticRules.put(ZenModeConfig.EVENTS_OBSOLETE_RULE_ID, rule);
+        ByteArrayOutputStream xmlBytes = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
+        TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
+
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules).doesNotContainKey(
+                ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void readXml_withEnabledEventsRule_keepsIt() throws Exception {
+        ZenRule rule = new ZenRule();
+        rule.id = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID;
+        rule.name = "Events";
+        rule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        rule.conditionId = Uri.parse("events");
+
+        rule.enabled = true;
+        mZenModeHelper.mConfig.automaticRules.put(ZenModeConfig.EVENTS_OBSOLETE_RULE_ID, rule);
+        ByteArrayOutputStream xmlBytes = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
+        TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
+
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules).containsKey(
+                ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
+    }
+
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
             @Nullable ZenPolicy zenPolicy) {
         ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index c7a136a..23ee893 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -44,7 +44,8 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -52,12 +53,14 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManagerInternal;
 import android.media.AudioManager;
 import android.os.Handler;
@@ -79,12 +82,10 @@
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.util.test.FakeSettingsProviderRule;
-import com.android.server.LocalServices;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.vibrator.VibrationSession.CallerInfo;
 import com.android.server.vibrator.VibrationSession.Status;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -101,8 +102,7 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
-    private static final int OLD_USER_ID = 123;
-    private static final int NEW_USER_ID = 456;
+    private static final int USER_ID = 123;
     private static final int UID = 1;
     private static final int VIRTUAL_DEVICE_ID = 1;
     private static final String SYSUI_PACKAGE_NAME = "sysui";
@@ -132,13 +132,12 @@
     @Mock private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
     @Mock private AudioManager mAudioManagerMock;
+    @Mock private IActivityManager mActivityManagerMock;
     @Mock private VibrationConfig mVibrationConfigMock;
 
     private TestLooper mTestLooper;
     private ContextWrapper mContextSpy;
     private VibrationSettings mVibrationSettings;
-    private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
-    private BroadcastReceiver mRegisteredBatteryBroadcastReceiver;
 
     @Before
     public void setUp() throws Exception {
@@ -146,24 +145,21 @@
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
-        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
-        when(mContextSpy.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManagerMock);
-        doAnswer(invocation -> {
-            mRegisteredPowerModeListener = invocation.getArgument(0);
-            return null;
-        }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
+        doReturn(contentResolver).when(mContextSpy).getContentResolver();
+
+        // Make sure broadcast receivers are not registered for this test, to avoid flakes.
+        doReturn(null).when(mContextSpy)
+                .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class), anyInt());
+
         when(mPackageManagerInternalMock.getSystemUiServiceComponent())
                 .thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));
 
-        removeServicesForTest();
-        addServicesForTest();
-
         setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
 
         setIgnoreVibrationsOnWirelessCharger(false);
-        createSystemReadyVibrationSettings();
-
         mockGoToSleep(/* goToSleepTime= */ 0, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+
+        createSystemReadyVibrationSettings();
     }
 
     private void createSystemReadyVibrationSettings() {
@@ -177,37 +173,18 @@
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        mVibrationSettings.onSystemReady();
-    }
-
-    private void removeServicesForTest() {
-        LocalServices.removeServiceForTest(PowerManagerInternal.class);
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
-    }
-
-    private void addServicesForTest() {
-        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
-        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
-        LocalServices.addService(VirtualDeviceManagerInternal.class,
-                mVirtualDeviceManagerInternalMock);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        removeServicesForTest();
+        mVibrationSettings.onSystemReady(mPackageManagerInternalMock, mPowerManagerInternalMock,
+                mActivityManagerMock, mVirtualDeviceManagerInternalMock, mAudioManagerMock);
     }
 
     @Test
     public void create_withOnlyRequiredSystemServices() {
-        // The only core services that we depend on are PowerManager and PackageManager
-        removeServicesForTest();
-        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
-        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
-
         VibrationSettings minimalVibrationSettings = new VibrationSettings(mContextSpy,
                 new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
-        minimalVibrationSettings.onSystemReady();
+
+        // The only core services that we depend on are Power, Package and Activity managers
+        minimalVibrationSettings.onSystemReady(mPackageManagerInternalMock,
+                mPowerManagerInternalMock, mActivityManagerMock, null, null);
     }
 
     @Test
@@ -215,8 +192,8 @@
         mVibrationSettings.addListener(mListenerMock);
 
         // Testing the broadcast flow manually.
-        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitching(USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(USER_ID);
 
         verify(mListenerMock, times(2)).onChange();
     }
@@ -226,9 +203,9 @@
         mVibrationSettings.addListener(mListenerMock);
 
         // Testing the broadcast flow manually.
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
         verify(mListenerMock, times(2)).onChange();
@@ -250,9 +227,9 @@
         mVibrationSettings.addListener(mListenerMock);
 
         // Testing the broadcast flow manually.
-        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-        mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
-        mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); // No change.
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); // Noop.
 
         verify(mListenerMock, times(2)).onChange();
     }
@@ -268,9 +245,9 @@
 
         // Trigger multiple observers manually.
         mVibrationSettings.mSettingObserver.onChange(false);
-        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(USER_ID);
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
         verifyNoMoreInteractions(mListenerMock);
@@ -311,11 +288,12 @@
 
     @Test
     public void wirelessChargingVibrationsEnabled_doesNotRegisterBatteryReceiver_allowsAnyUsage() {
-        setBatteryReceiverRegistrationResult(getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS));
         setIgnoreVibrationsOnWirelessCharger(false);
         createSystemReadyVibrationSettings();
 
-        assertNull(mRegisteredBatteryBroadcastReceiver);
+        verify(mContextSpy, never()).registerReceiver(any(BroadcastReceiver.class),
+                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
+
         for (int usage : ALL_USAGES) {
             assertVibrationNotIgnoredForUsage(usage);
         }
@@ -323,7 +301,6 @@
 
     @Test
     public void shouldIgnoreVibration_noBatteryIntentWhenSystemReady_allowsAnyUsage() {
-        setBatteryReceiverRegistrationResult(null);
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
@@ -335,7 +312,10 @@
     @Test
     public void shouldIgnoreVibration_onNonWirelessChargerWhenSystemReady_allowsAnyUsage() {
         Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
-        setBatteryReceiverRegistrationResult(nonWirelessChargingIntent);
+        doReturn(nonWirelessChargingIntent).when(mContextSpy).registerReceiver(
+                any(BroadcastReceiver.class),
+                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
+
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
@@ -347,7 +327,10 @@
     @Test
     public void shouldIgnoreVibration_onWirelessChargerWhenSystemReady_doesNotAllowFromAnyUsage() {
         Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
-        setBatteryReceiverRegistrationResult(wirelessChargingIntent);
+        doReturn(wirelessChargingIntent).when(mContextSpy).registerReceiver(
+                any(BroadcastReceiver.class),
+                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
+
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
@@ -358,13 +341,12 @@
 
     @Test
     public void shouldIgnoreVibration_receivesWirelessChargingIntent_doesNotAllowFromAnyUsage() {
-        Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
-        setBatteryReceiverRegistrationResult(nonWirelessChargingIntent);
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
 
         Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
-        mRegisteredBatteryBroadcastReceiver.onReceive(mContextSpy, wirelessChargingIntent);
+        mVibrationSettings.mBatteryBroadcastReceiver.onReceive(
+                mContextSpy, wirelessChargingIntent);
 
         for (int usage : ALL_USAGES) {
             assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
@@ -373,17 +355,21 @@
 
     @Test
     public void shouldIgnoreVibration_receivesNonWirelessChargingIntent_allowsAnyUsage() {
-        Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
-        setBatteryReceiverRegistrationResult(wirelessChargingIntent);
         setIgnoreVibrationsOnWirelessCharger(true);
         createSystemReadyVibrationSettings();
+
+        Intent wirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_WIRELESS);
+        mVibrationSettings.mBatteryBroadcastReceiver.onReceive(
+                mContextSpy, wirelessChargingIntent);
+
         // Check that initially, all usages are ignored due to the wireless charging.
         for (int usage : ALL_USAGES) {
             assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
         }
 
         Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
-        mRegisteredBatteryBroadcastReceiver.onReceive(mContextSpy, nonWirelessChargingIntent);
+        mVibrationSettings.mBatteryBroadcastReceiver.onReceive(
+                mContextSpy, nonWirelessChargingIntent);
 
         for (int usage : ALL_USAGES) {
             assertVibrationNotIgnoredForUsage(usage);
@@ -400,7 +386,7 @@
                 USAGE_HARDWARE_FEEDBACK
         ));
 
-        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
 
         for (int usage : ALL_USAGES) {
             if (expectedAllowedVibrations.contains(usage)) {
@@ -413,7 +399,7 @@
 
     @Test
     public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() {
-        mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
+        mVibrationSettings.mLowPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
 
         for (int usage : ALL_USAGES) {
             assertVibrationNotIgnoredForUsage(usage);
@@ -596,7 +582,7 @@
 
         // Testing the broadcast flow manually.
         when(mAudioManagerMock.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
         assertVibrationIgnoredForUsage(USAGE_RINGTONE, Status.IGNORED_FOR_RINGER_MODE);
@@ -852,16 +838,15 @@
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
 
         // Test early update of settings based on new user id.
-        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
-                NEW_USER_ID);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW, USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitching(USER_ID);
         assertEquals(VIBRATION_INTENSITY_LOW,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
 
         // Test later update of settings for UserHandle.USER_CURRENT.
         putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
                 UserHandle.USER_CURRENT);
-        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(USER_ID);
         assertEquals(VIBRATION_INTENSITY_LOW,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
     }
@@ -1009,7 +994,7 @@
     private void setRingerMode(int ringerMode) {
         when(mAudioManagerMock.getRingerModeInternal()).thenReturn(ringerMode);
         // Mock AudioManager broadcast of internal ringer mode change.
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+        mVibrationSettings.mRingerModeBroadcastReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
     }
 
@@ -1024,14 +1009,6 @@
         return new CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
     }
 
-    private void setBatteryReceiverRegistrationResult(Intent result) {
-        doAnswer(invocation -> {
-            mRegisteredBatteryBroadcastReceiver = invocation.getArgument(0);
-            return result;
-        }).when(mContextSpy).registerReceiver(any(BroadcastReceiver.class),
-                argThat(filter -> filter.matchAction(Intent.ACTION_BATTERY_CHANGED)), anyInt());
-    }
-
     private Intent getBatteryChangedIntent(int extraPluggedValue) {
         Intent batteryIntent = new Intent(Intent.ACTION_BATTERY_CHANGED);
         batteryIntent.putExtra(EXTRA_PLUGGED, extraPluggedValue);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 31cc50f1..e83a4b2 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -29,13 +29,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -96,8 +94,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.function.BooleanSupplier;
 import java.util.stream.Collectors;
 
@@ -187,11 +183,11 @@
         mVibratorProviders.clear();
         CombinedVibration effect = CombinedVibration.createParallel(
                 VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
+        verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
     }
 
     @Test
@@ -200,11 +196,11 @@
                 .addNext(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
                 .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
+        verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
     }
 
     @Test
@@ -212,34 +208,34 @@
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
         VibrationEffect effect = VibrationEffect.createOneShot(10, 100);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
     }
 
     @Test
     public void vibrate_oneShotWithoutAmplitudeControl_runsVibrationWithDefaultAmplitude() {
         VibrationEffect effect = VibrationEffect.createOneShot(10, 100);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
     }
 
@@ -249,17 +245,17 @@
 
         VibrationEffect effect = VibrationEffect.createWaveform(
                 new long[]{5, 5, 5}, new int[]{1, 2, 3}, -1);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(15)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(1, 2, 3),
                 mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
     }
@@ -277,13 +273,13 @@
         mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.5f);
         CompletableFuture<Void> mRequestVibrationParamsFuture = CompletableFuture.completedFuture(
                 null);
-        long vibrationId = startThreadAndDispatcher(effect, mRequestVibrationParamsFuture,
+        HalVibration vibration = startThreadAndDispatcher(effect, mRequestVibrationParamsFuture,
                 USAGE_RINGTONE);
         waitForCompletion();
 
         verify(mStatsLoggerMock, never()).logVibrationParamRequestTimeout(UID);
         assertEquals(Arrays.asList(expectedOneShot(15)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
         for (int i = 0; i < amplitudes.size(); i++) {
             assertTrue(amplitudes.get(i) < 1 / 255f);
@@ -301,12 +297,13 @@
                 new long[]{5, 5, 5}, new int[]{1, 1, 1}, -1);
 
         CompletableFuture<Void> neverCompletingFuture = new CompletableFuture<>();
-        long vibrationId = startThreadAndDispatcher(effect, neverCompletingFuture, USAGE_RINGTONE);
+        HalVibration vibration = startThreadAndDispatcher(effect, neverCompletingFuture,
+                USAGE_RINGTONE);
         waitForCompletion();
 
         verify(mStatsLoggerMock).logVibrationParamRequestTimeout(UID);
         assertEquals(Arrays.asList(expectedOneShot(15)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(1, 1, 1),
                 mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
     }
@@ -319,13 +316,13 @@
 
         int[] amplitudes = new int[]{1, 2, 3};
         VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5, 5, 5}, amplitudes, 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(
                 waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
                         TEST_TIMEOUT_MILLIS));
         // Vibration still running after 2 cycles.
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
         assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
 
         Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
@@ -333,15 +330,15 @@
                         /* uid= */ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
         mVibrationConductor.notifyCancelled(cancelVibrationInfo, /* immediate= */ false);
         waitForCompletion();
-        assertFalse(mThread.isRunningVibrationId(vibrationId));
+        assertFalse(mThread.isRunningVibrationId(vibration.id));
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verifyCallbacksTriggered(vibrationId, cancelVibrationInfo);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_SUPERSEDED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
-        assertFalse(fakeVibrator.getEffectSegments(vibrationId).isEmpty());
+        assertFalse(fakeVibrator.getEffectSegments(vibration.id).isEmpty());
         assertFalse(playedAmplitudes.isEmpty());
 
         for (int i = 0; i < playedAmplitudes.size(); i++) {
@@ -358,17 +355,17 @@
         int[] amplitudes = new int[]{1, 2, 3};
         VibrationEffect effect = VibrationEffect.createWaveform(
                 new long[]{1, 10, 100}, amplitudes, 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(expectedOneShot(5000)),
-                fakeVibrator.getEffectSegments(vibrationId));
+                fakeVibrator.getEffectSegments(vibration.id));
     }
 
     @Test
@@ -377,16 +374,16 @@
 
         VibrationEffect effect = VibrationEffect.createWaveform(
                 /* timings= */ new long[]{0, 100, 50, 100, 0, 0, 0, 50}, /* repeat= */ -1);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(300L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
 
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
-        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
+        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
                 .isEqualTo(expectedOneShots(100L, 150L));
     }
 
@@ -398,16 +395,16 @@
         VibrationEffect effect = VibrationEffect.createWaveform(
                 /* timings= */ new long[]{0, 100, 0, 50, 50, 0, 100, 50}, amplitudes,
                 /* repeat= */ -1);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(350L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
 
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
-        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
+        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
                 .isEqualTo(expectedOneShots(200L, 50L));
     }
 
@@ -420,7 +417,7 @@
 
         VibrationEffect effect = VibrationEffect.createWaveform(
                 /* timings= */ new long[]{0, 200, 50, 100, 0, 50, 50, 100}, /* repeat= */ 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         // We are expect this test to repeat the vibration effect twice, which would result in 5
         // segments being played:
         // 200ms ON
@@ -428,16 +425,17 @@
         // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms)
         // 150ms ON (100ms + 50ms, skips 0ms)
         // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms)
-        assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() >= 5,
+        assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() >= 5,
                 5000L + TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
-        assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).subList(0, 5))
+        assertThat(
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).subList(0, 5))
                 .isEqualTo(expectedOneShots(200L, 150L, 300L, 150L, 300L));
     }
 
@@ -458,18 +456,18 @@
         VibrationEffect repeatingEffect = VibrationEffect.startComposition()
                 .repeatEffectIndefinitely(effect)
                 .compose();
-        long vibrationId = startThreadAndDispatcher(repeatingEffect);
+        HalVibration vibration = startThreadAndDispatcher(repeatingEffect);
 
-        assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
+        assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
                 TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
         // PWLE size max was used to generate a single vibrate call with 10 segments.
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
-        assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
+        assertEquals(10, fakeVibrator.getEffectSegments(vibration.id).size());
     }
 
     @Test
@@ -487,18 +485,18 @@
         VibrationEffect repeatingEffect = VibrationEffect.startComposition()
                 .repeatEffectIndefinitely(effect)
                 .compose();
-        long vibrationId = startThreadAndDispatcher(repeatingEffect);
+        HalVibration vibration = startThreadAndDispatcher(repeatingEffect);
 
-        assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
+        assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
                 TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false);
         waitForCompletion();
 
         // Composition size max was used to generate a single vibrate call with 10 primitives.
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
-        assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
+        assertEquals(10, fakeVibrator.getEffectSegments(vibration.id).size());
     }
 
     @Test
@@ -510,17 +508,17 @@
         int[] amplitudes = new int[]{1, 2, 3};
         VibrationEffect effect = VibrationEffect.createWaveform(
                 new long[]{5000, 500, 50}, amplitudes, 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(expectedOneShot(5550)),
-                fakeVibrator.getEffectSegments(vibrationId));
+                fakeVibrator.getEffectSegments(vibration.id));
     }
 
     @LargeTest
@@ -534,17 +532,17 @@
         VibrationEffect effect = VibrationEffect.createWaveform(
                 /* timings= */ new long[]{expectedOnDuration - 100, 50},
                 /* amplitudes= */ new int[]{1, 2}, /* repeat= */ 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
-        assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1,
+        assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() > 1,
                 expectedOnDuration + TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
-        List<VibrationEffectSegment> effectSegments = fakeVibrator.getEffectSegments(vibrationId);
+        List<VibrationEffectSegment> effectSegments = fakeVibrator.getEffectSegments(vibration.id);
         // First time, turn vibrator ON for the expected fixed duration.
         assertEquals(expectedOnDuration, effectSegments.get(0).getDuration());
         // Vibrator turns off in the middle of the second execution of the first step. Expect it to
@@ -567,11 +565,11 @@
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
                 .compose();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
                 TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -584,7 +582,7 @@
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SETTINGS_UPDATE);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -597,11 +595,11 @@
         mVibratorProviders.get(VIBRATOR_ID).setVendorEffectDuration(10 * TEST_TIMEOUT_MILLIS);
 
         VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData());
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
                 TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -614,7 +612,7 @@
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SETTINGS_UPDATE);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -624,11 +622,11 @@
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
 
         VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
                 TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -641,7 +639,7 @@
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -650,17 +648,17 @@
         mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_THUD);
 
         VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_THUD);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
     }
 
     @Test
@@ -671,31 +669,31 @@
         HalVibration vibration = createVibration(CombinedVibration.createParallel(
                 VibrationEffect.get(VibrationEffect.EFFECT_CLICK)));
         vibration.addFallback(VibrationEffect.EFFECT_CLICK, fallback);
-        long vibrationId = startThreadAndDispatcher(vibration);
+        startThreadAndDispatcher(vibration);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
     }
 
     @Test
     public void vibrate_singleVibratorPrebakedAndUnsupportedEffect_ignoresVibration() {
         VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
         verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
-        assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
+        verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
+        assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
     }
 
     @Test
@@ -704,17 +702,17 @@
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
 
         VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData());
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID),
                 eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
-        assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
+        assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibration.id))
                 .containsExactly(effect)
                 .inOrder();
     }
@@ -730,18 +728,18 @@
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
                 .compose();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(
                 expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
                 expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
-                fakeVibrator.getEffectSegments(vibrationId));
+                fakeVibrator.getEffectSegments(vibration.id));
     }
 
     @Test
@@ -749,14 +747,14 @@
         VibrationEffect effect = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
                 .compose();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
         verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
-        assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
+        verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
+        assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
     }
 
     @Test
@@ -774,13 +772,13 @@
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
                 .compose();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         // Vibrator compose called twice.
-        verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        assertEquals(3, fakeVibrator.getEffectSegments(vibrationId).size());
+        verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        assertEquals(3, fakeVibrator.getEffectSegments(vibration.id).size());
     }
 
     @Test
@@ -810,14 +808,14 @@
                         .build())
                 .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
                 .compose();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         // Use first duration the vibrator is turned on since we cannot estimate the clicks.
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(
                 expectedOneShot(10),
@@ -829,7 +827,7 @@
                 expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.7f,
                         /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 120, /* duration= */ 20),
                 expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
     }
 
@@ -851,18 +849,18 @@
                 .compose();
         HalVibration vibration = createVibration(CombinedVibration.createParallel(effect));
         vibration.addFallback(VibrationEffect.EFFECT_TICK, fallback);
-        long vibrationId = startThreadAndDispatcher(vibration);
+        startThreadAndDispatcher(vibration);
         waitForCompletion();
 
         // Use first duration the vibrator is turned on since we cannot estimate the clicks.
         verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         List<VibrationEffectSegment> segments =
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId);
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id);
         assertTrue("Wrong segments: " + segments, segments.size() >= 4);
         assertTrue(segments.get(0) instanceof PrebakedSegment);
         assertTrue(segments.get(1) instanceof PrimitiveSegment);
@@ -891,13 +889,13 @@
                 .addSustain(Duration.ofMillis(30))
                 .addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
                 .build();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(
                 expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
@@ -907,8 +905,8 @@
                 expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f,
                         /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200,
                         /* duration= */ 40)),
-                fakeVibrator.getEffectSegments(vibrationId));
-        assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking(vibrationId));
+                fakeVibrator.getEffectSegments(vibration.id));
+        assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking(vibration.id));
     }
 
     @Test
@@ -932,15 +930,15 @@
                 .addTransition(Duration.ofMillis(40), targetAmplitude(0.7f), targetFrequency(200))
                 .addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
                 .build();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
 
         // Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
         // Using best split points instead of max-packing PWLEs.
-        verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        assertEquals(6, fakeVibrator.getEffectSegments(vibrationId).size());
+        verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        assertEquals(6, fakeVibrator.getEffectSegments(vibration.id).size());
     }
 
     @Test
@@ -949,28 +947,28 @@
         fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
         VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2, TEST_TIMEOUT_MILLIS));
         // Vibration still running after 2 cycles.
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
         assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
 
         mVibrationConductor.binderDied();
         waitForCompletion();
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BINDER_DIED);
     }
 
     @Test
     public void vibrate_singleVibrator_skipsSyncedCallbacks() {
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
-        long vibrationId = startThreadAndDispatcher(VibrationEffect.createOneShot(10, 100));
+        HalVibration vibration = startThreadAndDispatcher(VibrationEffect.createOneShot(10, 100));
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         verify(mManagerHooks, never()).prepareSyncedVibration(anyLong(), any());
         verify(mManagerHooks, never()).triggerSyncedVibration(anyLong());
         verify(mManagerHooks, never()).cancelSyncedVibration();
@@ -984,18 +982,18 @@
                 .addVibrator(VIBRATOR_ID, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
                 .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
     }
 
     @Test
@@ -1007,26 +1005,26 @@
 
         CombinedVibration effect = CombinedVibration.createParallel(
                 VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
-        verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
-        verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
+        verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
+        verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
 
         VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK);
         assertEquals(Arrays.asList(expected),
-                mVibratorProviders.get(1).getEffectSegments(vibrationId));
+                mVibratorProviders.get(1).getEffectSegments(vibration.id));
         assertEquals(Arrays.asList(expected),
-                mVibratorProviders.get(2).getEffectSegments(vibrationId));
+                mVibratorProviders.get(2).getEffectSegments(vibration.id));
         assertEquals(Arrays.asList(expected),
-                mVibratorProviders.get(3).getEffectSegments(vibrationId));
+                mVibratorProviders.get(3).getEffectSegments(vibration.id));
     }
 
     @Test
@@ -1049,32 +1047,32 @@
                         new long[]{10, 10}, new int[]{1, 2}, -1))
                 .addVibrator(4, composed)
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
-        verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
-        verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
-        verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
+        verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
+        verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+        verify(mControllerCallbacks).onComplete(eq(4), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
         assertFalse(mControllers.get(4).isVibrating());
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
-                mVibratorProviders.get(1).getEffectSegments(vibrationId));
+                mVibratorProviders.get(1).getEffectSegments(vibration.id));
         assertEquals(Arrays.asList(expectedOneShot(10)),
-                mVibratorProviders.get(2).getEffectSegments(vibrationId));
+                mVibratorProviders.get(2).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(100), mVibratorProviders.get(2).getAmplitudes());
         assertEquals(Arrays.asList(expectedOneShot(20)),
-                mVibratorProviders.get(3).getEffectSegments(vibrationId));
+                mVibratorProviders.get(3).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(1, 2), mVibratorProviders.get(3).getAmplitudes());
         assertEquals(Arrays.asList(
                 expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
-                mVibratorProviders.get(4).getEffectSegments(vibrationId));
+                mVibratorProviders.get(4).getEffectSegments(vibration.id));
     }
 
     @Test
@@ -1094,13 +1092,13 @@
                 .addNext(1, VibrationEffect.createOneShot(10, 100), /* delay= */ 50)
                 .addNext(2, composed, /* delay= */ 50)
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         waitForCompletion();
         InOrder controllerVerifier = inOrder(mControllerCallbacks);
-        controllerVerifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
-        controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
-        controllerVerifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
+        controllerVerifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+        controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
+        controllerVerifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
 
         InOrder batteryVerifier = inOrder(mManagerHooks);
         batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
@@ -1110,19 +1108,19 @@
         batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
         batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
 
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
-                mVibratorProviders.get(1).getEffectSegments(vibrationId));
+                mVibratorProviders.get(1).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes());
         assertEquals(Arrays.asList(
                 expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
-                mVibratorProviders.get(2).getEffectSegments(vibrationId));
+                mVibratorProviders.get(2).getEffectSegments(vibration.id));
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
-                mVibratorProviders.get(3).getEffectSegments(vibrationId));
+                mVibratorProviders.get(3).getEffectSegments(vibration.id));
     }
 
     @Test
@@ -1143,30 +1141,29 @@
         CombinedVibration effect = CombinedVibration.createParallel(composed);
         // We create the HalVibration here to obtain the vibration id and use it to mock the
         // required response when calling triggerSyncedVibration.
-        HalVibration halVibration = createVibration(effect);
-        long vibrationId = halVibration.id;
-        when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
-        startThreadAndDispatcher(halVibration);
+        HalVibration vibration = createVibration(effect);
+        when(mManagerHooks.triggerSyncedVibration(eq(vibration.id))).thenReturn(true);
+        startThreadAndDispatcher(vibration);
 
         assertTrue(waitUntil(
-                () -> !mVibratorProviders.get(1).getEffectSegments(vibrationId).isEmpty()
-                        && !mVibratorProviders.get(2).getEffectSegments(vibrationId).isEmpty(),
+                () -> !mVibratorProviders.get(1).getEffectSegments(vibration.id).isEmpty()
+                        && !mVibratorProviders.get(2).getEffectSegments(vibration.id).isEmpty(),
                 TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifySyncedVibrationComplete();
         waitForCompletion();
 
         long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_COMPOSE;
         verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibration.id));
         verify(mManagerHooks, never()).cancelSyncedVibration();
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
 
         VibrationEffectSegment expected = expectedPrimitive(
                 VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
         assertEquals(Arrays.asList(expected),
-                mVibratorProviders.get(1).getEffectSegments(vibrationId));
+                mVibratorProviders.get(1).getEffectSegments(vibration.id));
         assertEquals(Arrays.asList(expected),
-                mVibratorProviders.get(2).getEffectSegments(vibrationId));
+                mVibratorProviders.get(2).getEffectSegments(vibration.id));
     }
 
     @Test
@@ -1190,10 +1187,9 @@
                 .combine();
         // We create the HalVibration here to obtain the vibration id and use it to mock the
         // required response when calling triggerSyncedVibration.
-        HalVibration halVibration = createVibration(effect);
-        long vibrationId = halVibration.id;
-        when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(true);
-        startThreadAndDispatcher(halVibration);
+        HalVibration vibration = createVibration(effect);
+        when(mManagerHooks.triggerSyncedVibration(eq(vibration.id))).thenReturn(true);
+        startThreadAndDispatcher(vibration);
         waitForCompletion();
 
         long expectedCap = IVibratorManager.CAP_SYNC
@@ -1204,9 +1200,9 @@
                 | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM
                 | IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
         verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibration.id));
         verify(mManagerHooks, never()).cancelSyncedVibration();
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
     }
 
     @Test
@@ -1221,19 +1217,19 @@
                 .addVibrator(1, VibrationEffect.createOneShot(10, 100))
                 .addVibrator(2, VibrationEffect.createWaveform(new long[]{5}, new int[]{200}, -1))
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
         long expectedCap = IVibratorManager.CAP_SYNC | IVibratorManager.CAP_PREPARE_ON;
         verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mManagerHooks, never()).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks, never()).triggerSyncedVibration(eq(vibration.id));
         verify(mManagerHooks, never()).cancelSyncedVibration();
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
-                mVibratorProviders.get(1).getEffectSegments(vibrationId));
+                mVibratorProviders.get(1).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes());
         assertEquals(Arrays.asList(expectedOneShot(5)),
-                mVibratorProviders.get(2).getEffectSegments(vibrationId));
+                mVibratorProviders.get(2).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(200), mVibratorProviders.get(2).getAmplitudes());
     }
 
@@ -1250,10 +1246,9 @@
                 .combine();
         // We create the HalVibration here to obtain the vibration id and use it to mock the
         // required response when calling triggerSyncedVibration.
-        HalVibration halVibration = createVibration(effect);
-        long vibrationId = halVibration.id;
-        when(mManagerHooks.triggerSyncedVibration(eq(vibrationId))).thenReturn(false);
-        startThreadAndDispatcher(halVibration);
+        HalVibration vibration = createVibration(effect);
+        when(mManagerHooks.triggerSyncedVibration(eq(vibration.id))).thenReturn(false);
+        startThreadAndDispatcher(vibration);
         waitForCompletion();
 
         long expectedCap = IVibratorManager.CAP_SYNC
@@ -1262,7 +1257,7 @@
                 | IVibratorManager.CAP_MIXED_TRIGGER_ON
                 | IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
         verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
-        verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
+        verify(mManagerHooks).triggerSyncedVibration(eq(vibration.id));
         verify(mManagerHooks).cancelSyncedVibration();
         assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty());
     }
@@ -1282,7 +1277,7 @@
                 .addVibrator(3, VibrationEffect.createWaveform(
                         new long[]{60}, new int[]{6}, -1))
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         // All vibrators are turned on in parallel.
         assertTrue(waitUntil(
@@ -1295,20 +1290,20 @@
 
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(80L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
-        verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
-        verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
-        verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
+        verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
+        verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(25)),
-                mVibratorProviders.get(1).getEffectSegments(vibrationId));
+                mVibratorProviders.get(1).getEffectSegments(vibration.id));
         assertEquals(Arrays.asList(expectedOneShot(80)),
-                mVibratorProviders.get(2).getEffectSegments(vibrationId));
+                mVibratorProviders.get(2).getEffectSegments(vibration.id));
         assertEquals(Arrays.asList(expectedOneShot(60)),
-                mVibratorProviders.get(3).getEffectSegments(vibrationId));
+                mVibratorProviders.get(3).getEffectSegments(vibration.id));
         assertEquals(expectedAmplitudes(1, 2, 3), mVibratorProviders.get(1).getAmplitudes());
         assertEquals(expectedAmplitudes(4, 5), mVibratorProviders.get(2).getAmplitudes());
         assertEquals(expectedAmplitudes(6), mVibratorProviders.get(3).getAmplitudes());
@@ -1327,17 +1322,11 @@
                 CombinedVibration.createParallel(
                         VibrationEffect.createOneShot(
                                 expectedDuration, VibrationEffect.DEFAULT_AMPLITUDE)));
-        CountDownLatch vibrationCompleteLatch = new CountDownLatch(1);
-        doAnswer(unused -> {
-            vibrationCompleteLatch.countDown();
-            return null;
-        }).when(mManagerHooks).onVibrationCompleted(eq(vibration.id), any());
 
         startThreadAndDispatcher(vibration);
         long startTime = SystemClock.elapsedRealtime();
 
-        assertTrue(vibrationCompleteLatch.await(expectedDuration + TEST_TIMEOUT_MILLIS,
-                TimeUnit.MILLISECONDS));
+        vibration.waitForEnd();
         long vibrationEndTime = SystemClock.elapsedRealtime();
 
         waitForCompletion(rampDownDuration + TEST_TIMEOUT_MILLIS);
@@ -1363,17 +1352,11 @@
                 CombinedVibration.createParallel(
                         VibrationEffect.createOneShot(
                                 expectedDuration, VibrationEffect.DEFAULT_AMPLITUDE)));
-        CountDownLatch vibrationCompleteLatch = new CountDownLatch(1);
-        doAnswer(unused -> {
-            vibrationCompleteLatch.countDown();
-            return null;
-        }).when(mManagerHooks).onVibrationCompleted(eq(vibration.id), any());
 
         startThreadAndDispatcher(vibration);
         long startTime = SystemClock.elapsedRealtime();
 
-        assertTrue(vibrationCompleteLatch.await(callbackDelay + TEST_TIMEOUT_MILLIS,
-                TimeUnit.MILLISECONDS));
+        vibration.waitForEnd();
         long vibrationEndTime = SystemClock.elapsedRealtime();
 
         waitForCompletion(TEST_TIMEOUT_MILLIS);
@@ -1397,17 +1380,11 @@
                 CombinedVibration.createParallel(
                         VibrationEffect.createOneShot(
                                 expectedDuration, VibrationEffect.DEFAULT_AMPLITUDE)));
-        CountDownLatch vibrationCompleteLatch = new CountDownLatch(1);
-        doAnswer(unused -> {
-            vibrationCompleteLatch.countDown();
-            return null;
-        }).when(mManagerHooks).onVibrationCompleted(eq(vibration.id), any());
 
         startThreadAndDispatcher(vibration);
         long startTime = SystemClock.elapsedRealtime();
 
-        assertTrue(vibrationCompleteLatch.await(callbackTimeout + TEST_TIMEOUT_MILLIS,
-                TimeUnit.MILLISECONDS));
+        vibration.waitForEnd();
         long vibrationEndTime = SystemClock.elapsedRealtime();
 
         waitForCompletion(callbackDelay + TEST_TIMEOUT_MILLIS);
@@ -1461,11 +1438,11 @@
         fakeVibrator.setOnLatency(latency);
 
         VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
-        assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
+        assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
                 TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(cancellingThread).
@@ -1480,7 +1457,7 @@
 
         // After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
         waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS);
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -1500,10 +1477,10 @@
                         .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
                         .compose())
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -1516,7 +1493,7 @@
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
     }
@@ -1534,10 +1511,10 @@
                 .addVibrator(1, VibrationEffect.createVendorEffect(createTestVendorData()))
                 .addVibrator(2, VibrationEffect.createVendorEffect(createTestVendorData()))
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -1550,7 +1527,7 @@
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
     }
@@ -1566,12 +1543,12 @@
                         new long[]{100, 100}, new int[]{1, 2}, 0))
                 .addVibrator(2, VibrationEffect.createOneShot(100, 100))
                 .combine();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> mControllers.get(1).isVibrating()
                         && mControllers.get(2).isVibrating(),
                 TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -1584,7 +1561,7 @@
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
     }
@@ -1592,19 +1569,17 @@
     @Test
     public void vibrate_binderDied_cancelsVibration() throws Exception {
         VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
                 TEST_TIMEOUT_MILLIS));
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         mVibrationConductor.binderDied();
         waitForCompletion();
 
-        verify(mVibrationToken).linkToDeath(same(mVibrationConductor), eq(0));
-        verify(mVibrationToken).unlinkToDeath(same(mVibrationConductor), eq(0));
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED);
-        assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BINDER_DIED);
+        assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -1615,15 +1590,15 @@
 
         VibrationEffect effect = VibrationEffect.createWaveform(
                 new long[]{5, 5, 5}, new int[]{60, 120, 240}, -1);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
 
         // Duration extended for 5 + 5 + 5 + 15.
         assertEquals(Arrays.asList(expectedOneShot(30)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
         assertTrue(amplitudes.size() > 3);
         assertEquals(expectedAmplitudes(60, 120, 240), amplitudes.subList(0, 3));
@@ -1633,24 +1608,24 @@
     }
 
     @Test
-    public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds() {
+    public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds()
+            throws Exception {
         when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(10_000);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
         VibrationEffect effect = VibrationEffect.createOneShot(10, 200);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
 
         // Vibration completed but vibrator not yet released.
-        verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
-                eq(new Vibration.EndInfo(Status.FINISHED)));
+        vibration.waitForEnd();
         verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong());
 
         // Thread still running ramp down.
-        assertTrue(mThread.isRunningVibrationId(vibrationId));
+        assertTrue(mThread.isRunningVibrationId(vibration.id));
 
         // Duration extended for 10 + 10000.
         assertEquals(Arrays.asList(expectedOneShot(10_010)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
 
         // Will stop the ramp down right away.
         mVibrationConductor.notifyCancelled(
@@ -1658,9 +1633,8 @@
         waitForCompletion();
 
         // Does not cancel already finished vibration, but releases vibrator.
-        verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
-                eq(new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE)));
-        verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
+        assertThat(vibration.getStatus()).isNotEqualTo(Status.CANCELLED_BY_SETTINGS_UPDATE);
+        verify(mManagerHooks).onVibrationThreadReleased(vibration.id);
     }
 
     @Test
@@ -1670,18 +1644,18 @@
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
         VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
                 TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
 
         // Duration extended for 10000 + 15.
         assertEquals(Arrays.asList(expectedOneShot(10_015)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
         assertTrue(amplitudes.size() > 1);
         for (int i = 1; i < amplitudes.size(); i++) {
@@ -1696,14 +1670,14 @@
         mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
 
         VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
     }
 
@@ -1714,13 +1688,13 @@
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
 
         VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData());
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
 
-        assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
+        assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibration.id))
                 .containsExactly(effect)
                 .inOrder();
         assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty();
@@ -1737,15 +1711,15 @@
         VibrationEffect effect = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
                 .compose();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
 
         assertEquals(
                 Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
+                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
         assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
     }
 
@@ -1764,14 +1738,14 @@
         VibrationEffect effect = VibrationEffect.startWaveform()
                 .addTransition(Duration.ofMillis(1), targetAmplitude(1))
                 .build();
-        long vibrationId = startThreadAndDispatcher(effect);
+        HalVibration vibration = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+        verifyCallbacksTriggered(vibration, Status.FINISHED);
 
         assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)),
-                fakeVibrator.getEffectSegments(vibrationId));
+                fakeVibrator.getEffectSegments(vibration.id));
         assertTrue(fakeVibrator.getAmplitudes().isEmpty());
     }
 
@@ -1796,69 +1770,68 @@
         VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100);
         VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
 
-        long vibrationId1 = startThreadAndDispatcher(effect1);
+        HalVibration vibration1 = startThreadAndDispatcher(effect1);
         waitForCompletion();
-        verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1);
-        verifyCallbacksTriggered(vibrationId1, Status.FINISHED);
 
-        long vibrationId2 = startThreadAndDispatcher(effect2);
+        HalVibration vibration2 = startThreadAndDispatcher(effect2);
         // Effect2 won't complete on its own. Cancel it after a couple of repeats.
         Thread.sleep(150);  // More than two TICKs.
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        long vibrationId3 = startThreadAndDispatcher(effect3);
+        HalVibration vibration3 = startThreadAndDispatcher(effect3);
         waitForCompletion();
 
         // Effect4 is a long oneshot, but it gets cancelled as fast as possible.
         long start4 = System.currentTimeMillis();
-        long vibrationId4 = startThreadAndDispatcher(effect4);
+        HalVibration vibration4 = startThreadAndDispatcher(effect4);
         mVibrationConductor.notifyCancelled(
                 new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ true);
         waitForCompletion();
         long duration4 = System.currentTimeMillis() - start4;
 
         // Effect5 is to show that things keep going after the immediate cancel.
-        long vibrationId5 = startThreadAndDispatcher(effect5);
+        HalVibration vibration5 = startThreadAndDispatcher(effect5);
         waitForCompletion();
 
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         // Effect1
-        verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1);
-        verifyCallbacksTriggered(vibrationId1, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibration1.id);
+        verifyCallbacksTriggered(vibration1, Status.FINISHED);
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
-                fakeVibrator.getEffectSegments(vibrationId1));
+                fakeVibrator.getEffectSegments(vibration1.id));
 
         // Effect2: repeating, cancelled.
-        verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibrationId2);
-        verifyCallbacksTriggered(vibrationId2, Status.CANCELLED_BY_USER);
+        verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibration2.id);
+        verifyCallbacksTriggered(vibration2, Status.CANCELLED_BY_USER);
 
         // The exact count of segments might vary, so just check that there's more than 2 and
         // all elements are the same segment.
-        List<VibrationEffectSegment> actualSegments2 = fakeVibrator.getEffectSegments(vibrationId2);
+        List<VibrationEffectSegment> actualSegments2 =
+                fakeVibrator.getEffectSegments(vibration2.id);
         assertTrue(actualSegments2.size() + " > 2", actualSegments2.size() > 2);
         for (VibrationEffectSegment segment : actualSegments2) {
             assertEquals(expectedPrebaked(VibrationEffect.EFFECT_TICK), segment);
         }
 
         // Effect3
-        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId3));
-        verifyCallbacksTriggered(vibrationId3, Status.FINISHED);
+        verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration3.id));
+        verifyCallbacksTriggered(vibration3, Status.FINISHED);
         assertEquals(Arrays.asList(
                         expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
-                fakeVibrator.getEffectSegments(vibrationId3));
+                fakeVibrator.getEffectSegments(vibration3.id));
 
         // Effect4: cancelled quickly.
-        verifyCallbacksTriggered(vibrationId4, Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibration4, Status.CANCELLED_BY_SCREEN_OFF);
         assertTrue("Tested duration=" + duration4, duration4 < 2000);
 
         // Effect5: played normally after effect4, which may or may not have played.
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
-                fakeVibrator.getEffectSegments(vibrationId5));
+                fakeVibrator.getEffectSegments(vibration5.id));
     }
 
     private void mockVibrators(int... vibratorIds) {
@@ -1875,19 +1848,19 @@
         mVibrationSettings.mSettingObserver.onChange(false);
     }
 
-    private long startThreadAndDispatcher(VibrationEffect effect) {
+    private HalVibration startThreadAndDispatcher(VibrationEffect effect) {
         return startThreadAndDispatcher(CombinedVibration.createParallel(effect));
     }
 
-    private long startThreadAndDispatcher(CombinedVibration effect) {
+    private HalVibration startThreadAndDispatcher(CombinedVibration effect) {
         return startThreadAndDispatcher(createVibration(effect));
     }
 
-    private long startThreadAndDispatcher(HalVibration vib) {
+    private HalVibration startThreadAndDispatcher(HalVibration vib) {
         return startThreadAndDispatcher(vib, /* requestVibrationParamsFuture= */ null);
     }
 
-    private long startThreadAndDispatcher(VibrationEffect effect,
+    private HalVibration startThreadAndDispatcher(VibrationEffect effect,
             CompletableFuture<Void> requestVibrationParamsFuture, int usage) {
         VibrationAttributes attrs = new VibrationAttributes.Builder()
                 .setUsage(usage)
@@ -1898,14 +1871,14 @@
         return startThreadAndDispatcher(vib, requestVibrationParamsFuture);
     }
 
-    private long startThreadAndDispatcher(HalVibration vib,
+    private HalVibration startThreadAndDispatcher(HalVibration vib,
             CompletableFuture<Void> requestVibrationParamsFuture) {
         mControllers = createVibratorControllers();
         DeviceAdapter deviceAdapter = new DeviceAdapter(mVibrationSettings, mControllers);
         mVibrationConductor = new VibrationStepConductor(vib, mVibrationSettings, deviceAdapter,
                 mVibrationScaler, mStatsLoggerMock, requestVibrationParamsFuture, mManagerHooks);
         assertTrue(mThread.runVibrationOnVibrationThread(mVibrationConductor));
-        return mVibrationConductor.getVibration().id;
+        return mVibrationConductor.getVibration();
     }
 
     private boolean waitUntil(BooleanSupplier predicate, long timeout)
@@ -1994,13 +1967,9 @@
                 .collect(Collectors.toList());
     }
 
-    private void verifyCallbacksTriggered(long vibrationId, Status expectedStatus) {
-        verifyCallbacksTriggered(vibrationId, new Vibration.EndInfo(expectedStatus));
-    }
-
-    private void verifyCallbacksTriggered(long vibrationId, Vibration.EndInfo expectedEndInfo) {
-        verify(mManagerHooks).onVibrationCompleted(eq(vibrationId), eq(expectedEndInfo));
-        verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
+    private void verifyCallbacksTriggered(HalVibration vibration, Status expectedStatus) {
+        assertThat(vibration.getStatus()).isEqualTo(expectedStatus);
+        verify(mManagerHooks).onVibrationThreadReleased(vibration.id);
     }
 
     private static final class TestLooperAutoDispatcher extends Thread {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
index 0d13be6..e8ca8bf 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -127,13 +127,13 @@
     public void setExternalControl_withCapability_enablesExternalControl() {
         mockVibratorCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
         VibratorController controller = createController();
-        assertFalse(controller.isUnderExternalControl());
+        assertFalse(controller.isVibrating());
 
         controller.setExternalControl(true);
-        assertTrue(controller.isUnderExternalControl());
+        assertTrue(controller.isVibrating());
 
         controller.setExternalControl(false);
-        assertFalse(controller.isUnderExternalControl());
+        assertFalse(controller.isVibrating());
 
         InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
         inOrderVerifier.verify(mNativeWrapperMock).setExternalControl(eq(true));
@@ -143,10 +143,10 @@
     @Test
     public void setExternalControl_withNoCapability_ignoresExternalControl() {
         VibratorController controller = createController();
-        assertFalse(controller.isUnderExternalControl());
+        assertFalse(controller.isVibrating());
 
         controller.setExternalControl(true);
-        assertFalse(controller.isUnderExternalControl());
+        assertFalse(controller.isVibrating());
 
         verify(mNativeWrapperMock, never()).setExternalControl(anyBoolean());
     }
@@ -181,6 +181,38 @@
     }
 
     @Test
+    public void setAmplitude_vibratorIdle_ignoresAmplitude() {
+        VibratorController controller = createController();
+        assertFalse(controller.isVibrating());
+
+        controller.setAmplitude(1);
+        assertEquals(0, controller.getCurrentAmplitude(), /* delta= */ 0);
+    }
+
+    @Test
+    public void setAmplitude_vibratorUnderExternalControl_ignoresAmplitude() {
+        mockVibratorCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+        VibratorController controller = createController();
+        controller.setExternalControl(true);
+        assertTrue(controller.isVibrating());
+
+        controller.setAmplitude(1);
+        assertEquals(0, controller.getCurrentAmplitude(), /* delta= */ 0);
+    }
+
+    @Test
+    public void setAmplitude_vibratorVibrating_setsAmplitude() {
+        when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+        VibratorController controller = createController();
+        controller.on(100, /* vibrationId= */ 1);
+        assertTrue(controller.isVibrating());
+        assertEquals(-1, controller.getCurrentAmplitude(), /* delta= */ 0);
+
+        controller.setAmplitude(1);
+        assertEquals(1, controller.getCurrentAmplitude(), /* delta= */ 0);
+    }
+
+    @Test
     public void on_withDuration_turnsVibratorOn() {
         when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
         VibratorController controller = createController();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 4012575..538c3fc 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -266,12 +266,13 @@
     @After
     public void tearDown() throws Exception {
         if (mService != null) {
-            if (!mPendingVibrations.stream().allMatch(HalVibration::hasEnded)) {
-                // Cancel any pending vibration from tests.
-                cancelVibrate(mService);
-                for (HalVibration vibration : mPendingVibrations) {
-                    vibration.waitForEnd();
-                }
+            // Make sure we have permission to cancel test vibrations, even if the test denied them.
+            grantPermission(android.Manifest.permission.VIBRATE);
+            // Cancel any pending vibration from tests, including external vibrations.
+            cancelVibrate(mService);
+            // Wait until pending vibrations end asynchronously.
+            for (HalVibration vibration : mPendingVibrations) {
+                vibration.waitForEnd();
             }
             // Wait until all vibrators have stopped vibrating, waiting for ramp-down.
             // Note: if a test is flaky here something is wrong with the vibration finalization.
@@ -1538,7 +1539,6 @@
         PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0);
         assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId());
         VibrationAttributes attrs = vibration.callerInfo.attrs;
-        assertEquals(VibrationAttributes.USAGE_HARDWARE_FEEDBACK, attrs.getUsage());
         assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
         assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
     }
@@ -1560,11 +1560,11 @@
                 HapticFeedbackConstants.SCROLL_TICK,
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
         mHapticFeedbackVibrationMapSourceTouchScreen.put(
-                HapticFeedbackConstants.DRAG_START,
-                VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
+                HapticFeedbackConstants.SCROLL_ITEM_FOCUS,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD));
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
-        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK);
+        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_THUD);
         VibratorManagerService service = createSystemReadyService();
 
         HalVibration vibrationByRotary =
@@ -1573,7 +1573,7 @@
                         InputDevice.SOURCE_ROTARY_ENCODER, /* always= */ true);
         HalVibration vibrationByTouchScreen =
                 performHapticFeedbackForInputDeviceAndWaitUntilFinished(
-                        service, HapticFeedbackConstants.DRAG_START, /* inputDeviceId= */ 0,
+                        service, HapticFeedbackConstants.SCROLL_ITEM_FOCUS, /* inputDeviceId= */ 0,
                         InputDevice.SOURCE_TOUCHSCREEN, /* always= */ true);
 
         List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
@@ -1583,18 +1583,17 @@
         PrebakedSegment segmentByRotary = (PrebakedSegment) playedSegments.get(0);
         assertEquals(VibrationEffect.EFFECT_CLICK, segmentByRotary.getEffectId());
         VibrationAttributes attrsByRotary = vibrationByRotary.callerInfo.attrs;
-        assertEquals(VibrationAttributes.USAGE_HARDWARE_FEEDBACK, attrsByRotary.getUsage());
         assertTrue(attrsByRotary.isFlagSet(
                 VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
         assertTrue(attrsByRotary.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
         // Verify feedback by touch screen input
         PrebakedSegment segmentByTouchScreen = (PrebakedSegment) playedSegments.get(1);
-        assertEquals(VibrationEffect.EFFECT_TICK, segmentByTouchScreen.getEffectId());
+        assertEquals(VibrationEffect.EFFECT_THUD, segmentByTouchScreen.getEffectId());
         VibrationAttributes attrsByTouchScreen = vibrationByTouchScreen.callerInfo.attrs;
-        assertEquals(VibrationAttributes.USAGE_TOUCH, attrsByTouchScreen.getUsage());
-        assertTrue(attrsByRotary.isFlagSet(
+        assertTrue(attrsByTouchScreen.isFlagSet(
                 VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
-        assertTrue(attrsByRotary.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
+        assertTrue(
+                attrsByTouchScreen.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
     }
 
     @Test
@@ -2244,7 +2243,7 @@
         VibratorManagerService service = createSystemReadyService();
 
         VibrationEffect effect = VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100);
-        vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+        HalVibration vibration = vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
 
         // VibrationThread will start this vibration async, so wait until vibration is triggered.
         assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -2257,7 +2256,8 @@
         assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         // Vibration is cancelled.
-        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+        vibration.waitForEnd();
+        assertThat(vibration.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
         assertEquals(Arrays.asList(false, true),
                 mVibratorProviders.get(1).getExternalControlStates());
     }
@@ -2298,7 +2298,7 @@
 
         VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
                 new long[]{100, 200, 300}, new int[]{128, 255, 255}, 1);
-        vibrate(service, repeatingEffect, ALARM_ATTRS);
+        HalVibration repeatingVibration = vibrate(service, repeatingEffect, ALARM_ATTRS);
 
         // VibrationThread will start this vibration async, so wait until vibration is triggered.
         assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -2310,7 +2310,8 @@
         assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
 
         // Vibration is cancelled.
-        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+        repeatingVibration.waitForEnd();
+        assertThat(repeatingVibration.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
         assertEquals(Arrays.asList(false, true),
                 mVibratorProviders.get(1).getExternalControlStates());
     }
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 1c33116..6f9c890 100644
--- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
@@ -22,6 +22,7 @@
 import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS;
 import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
 
+import android.platform.test.annotations.DisableFlags;
 import android.view.ViewConfiguration;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -39,6 +40,7 @@
  */
 @MediumTest
 @RunWith(AndroidJUnit4.class)
+@DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_PRESS_GESTURES)
 public class CombinationKeyTests extends ShortcutKeyTestBase {
     private static final long A11Y_KEY_HOLD_MILLIS = 3500;
 
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 487390d..8b5f68a 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationManagerTests.java
@@ -71,12 +71,12 @@
                 new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN,
                         KEYCODE_POWER) {
                     @Override
-                    void execute() {
+                    public void execute() {
                         mAction1Triggered.countDown();
                     }
 
                     @Override
-                    void cancel() {
+                    public void cancel() {
                     }
                 });
 
@@ -85,21 +85,21 @@
                 new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN,
                         KEYCODE_VOLUME_UP) {
                     @Override
-                    boolean preCondition() {
+                    public boolean preCondition() {
                         return mPreCondition;
                     }
 
                     @Override
-                    void execute() {
+                    public void execute() {
                         mAction2Triggered.countDown();
                     }
 
                     @Override
-                    void cancel() {
+                    public void cancel() {
                     }
 
                     @Override
-                    long getKeyInterceptDelayMs() {
+                    public long getKeyInterceptDelayMs() {
                         return 0;
                     }
                 });
@@ -115,12 +115,12 @@
                     };
 
                     @Override
-                    void execute() {
+                    public void execute() {
                         mHandler.postDelayed(mAction, SCHEDULE_TIME);
                     }
 
                     @Override
-                    void cancel() {
+                    public void cancel() {
                         mHandler.removeCallbacks(mAction);
                     }
                 });
@@ -235,12 +235,12 @@
                 new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN,
                         KEYCODE_POWER) {
                     @Override
-                    void execute() {
+                    public void execute() {
                         mAction1Triggered.countDown();
                     }
 
                     @Override
-                    void cancel() {
+                    public void cancel() {
                     }
                 };
 
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 3d978e4..cdb4542 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -22,10 +22,13 @@
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
+import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS;
+import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
 import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
 
 import android.hardware.input.KeyGestureEvent;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.view.KeyEvent;
@@ -56,88 +59,15 @@
     private static final int CTRL_ON = MODIFIER.get(KeyEvent.KEYCODE_CTRL_LEFT);
 
     @Keep
-    private static Object[][] shortcutTestArguments() {
+    private static Object[][] shortcutTestArgumentsNotMigratedToKeyGestureController() {
         // testName, testKeys, expectedKeyGestureType, expectedKey, expectedModifierState
         return new Object[][]{
-                {"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_H, META_ON},
-                {"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_ENTER,
-                        META_ON},
                 {"HOME key -> Open Home", new int[]{KeyEvent.KEYCODE_HOME},
                         KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
                         KeyEvent.KEYCODE_HOME, 0},
-                {"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
-                        KeyEvent.KEYCODE_RECENT_APPS, 0},
-                {"Meta + Tab -> Open Overview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
-                        META_ON},
-                {"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
-                        ALT_ON},
                 {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK},
                         KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
                         KeyEvent.KEYCODE_BACK, 0},
-                {"Meta + Escape -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_ESCAPE},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_ESCAPE,
-                        META_ON},
-                {"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DPAD_LEFT,
-                        META_ON},
-                {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DEL, META_ON},
-                {"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
-                        KeyEvent.KEYCODE_APP_SWITCH, 0},
-                {"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
-                        KeyEvent.KEYCODE_ASSIST, 0},
-                {"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A,
-                        META_ON},
-                {"VOICE_ASSIST key -> Launch Voice Assistant",
-                        new int[]{KeyEvent.KEYCODE_VOICE_ASSIST},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
-                        KeyEvent.KEYCODE_VOICE_ASSIST, 0},
-                {"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
-                        KeyEvent.KEYCODE_I, META_ON},
-                {"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_N, META_ON},
-                {"NOTIFICATION key -> Toggle Notification Panel",
-                        new int[]{KeyEvent.KEYCODE_NOTIFICATION},
-                        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},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, KeyEvent.KEYCODE_S,
-                        META_ON | CTRL_ON},
-                {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
-                        KeyEvent.KEYCODE_SLASH, META_ON},
-                {"BRIGHTNESS_UP key -> Increase Brightness",
-                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
-                        KeyEvent.KEYCODE_BRIGHTNESS_UP, 0},
-                {"BRIGHTNESS_DOWN key -> Decrease Brightness",
-                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
-                        KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0},
-                {"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight",
-                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
-                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0},
-                {"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight",
-                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
-                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0},
-                {"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight",
-                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
-                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0},
                 {"VOLUME_UP key -> Increase Volume", new int[]{KeyEvent.KEYCODE_VOLUME_UP},
                         KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_UP,
                         KeyEvent.KEYCODE_VOLUME_UP, 0},
@@ -147,53 +77,9 @@
                 {"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
                         KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE,
                         KeyEvent.KEYCODE_VOLUME_MUTE, 0},
-                {"ALL_APPS key -> Open App Drawer",
-                        new int[]{KeyEvent.KEYCODE_ALL_APPS},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
-                        KeyEvent.KEYCODE_ALL_APPS, 0},
-                {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
-                        KeyEvent.KEYCODE_SEARCH, 0},
-                {"LANGUAGE_SWITCH key -> Switch Keyboard Language",
-                        new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
-                        KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
-                {"META key -> Open App Drawer", new int[]{META_KEY},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS, META_KEY,
-                        META_ON},
-                {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, ALT_KEY,
-                        META_ON | ALT_ON},
-                {"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, META_KEY,
-                        META_ON | ALT_ON},
-                {"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
-                        KeyEvent.KEYCODE_CAPS_LOCK, 0},
                 {"MUTE key -> Mute System Microphone", new int[]{KeyEvent.KEYCODE_MUTE},
                         KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE,
                         0},
-                {"Meta + Ctrl + DPAD_UP -> Split screen navigation",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
-                        KeyEvent.KEYCODE_DPAD_UP,
-                        META_ON | CTRL_ON},
-                {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
-                        KeyEvent.KEYCODE_DPAD_LEFT,
-                        META_ON | CTRL_ON},
-                {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
-                        KeyEvent.KEYCODE_DPAD_RIGHT,
-                        META_ON | CTRL_ON},
-                {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, KeyEvent.KEYCODE_L,
-                        META_ON},
-                {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
-                        KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyEvent.KEYCODE_N,
-                        META_ON | CTRL_ON},
                 {"POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_POWER},
                         KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER, KeyEvent.KEYCODE_POWER,
                         0},
@@ -279,7 +165,130 @@
                 {"Meta + S -> Launch Default Messaging App",
                         new int[]{META_KEY, KeyEvent.KEYCODE_S},
                         KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
-                        KeyEvent.KEYCODE_S, META_ON},
+                        KeyEvent.KEYCODE_S, META_ON}};
+    }
+
+    @Keep
+    private static Object[][] shortcutTestArgumentsMigratedToKeyGestureController() {
+        // testName, testKeys, expectedKeyGestureType, expectedKey, expectedModifierState
+        return new Object[][]{
+                {"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_H, META_ON},
+                {"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_ENTER,
+                        META_ON},
+                {"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+                        KeyEvent.KEYCODE_RECENT_APPS, 0},
+                {"Meta + Tab -> Open Overview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
+                        META_ON},
+                {"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
+                        ALT_ON},
+                {"Meta + Escape -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_ESCAPE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_ESCAPE,
+                        META_ON},
+                {"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DPAD_LEFT,
+                        META_ON},
+                {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DEL, META_ON},
+                {"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+                        KeyEvent.KEYCODE_APP_SWITCH, 0},
+                {"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+                        KeyEvent.KEYCODE_ASSIST, 0},
+                {"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A,
+                        META_ON},
+                {"VOICE_ASSIST key -> Launch Voice Assistant",
+                        new int[]{KeyEvent.KEYCODE_VOICE_ASSIST},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
+                        KeyEvent.KEYCODE_VOICE_ASSIST, 0},
+                {"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+                        KeyEvent.KEYCODE_I, META_ON},
+                {"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                        KeyEvent.KEYCODE_N, META_ON},
+                {"NOTIFICATION key -> Toggle Notification Panel",
+                        new int[]{KeyEvent.KEYCODE_NOTIFICATION},
+                        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},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, KeyEvent.KEYCODE_S,
+                        META_ON | CTRL_ON},
+                {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+                        KeyEvent.KEYCODE_SLASH, META_ON},
+                {"BRIGHTNESS_UP key -> Increase Brightness",
+                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+                        KeyEvent.KEYCODE_BRIGHTNESS_UP, 0},
+                {"BRIGHTNESS_DOWN key -> Decrease Brightness",
+                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+                        KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0},
+                {"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0},
+                {"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0},
+                {"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0},
+                {"ALL_APPS key -> Open App Drawer",
+                        new int[]{KeyEvent.KEYCODE_ALL_APPS},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
+                        KeyEvent.KEYCODE_ALL_APPS, 0},
+                {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+                        KeyEvent.KEYCODE_SEARCH, 0},
+                {"LANGUAGE_SWITCH key -> Switch Keyboard Language",
+                        new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                        KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
+                {"META key -> Open App Drawer", new int[]{META_KEY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS, META_KEY,
+                        META_ON},
+                {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, ALT_KEY,
+                        META_ON | ALT_ON},
+                {"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, META_KEY,
+                        META_ON | ALT_ON},
+                {"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+                        KeyEvent.KEYCODE_CAPS_LOCK, 0},
+                {"Meta + Ctrl + DPAD_UP -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+                        KeyEvent.KEYCODE_DPAD_UP,
+                        META_ON | CTRL_ON},
+                {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+                        KeyEvent.KEYCODE_DPAD_LEFT,
+                        META_ON | CTRL_ON},
+                {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+                        KeyEvent.KEYCODE_DPAD_RIGHT,
+                        META_ON | CTRL_ON},
+                {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, KeyEvent.KEYCODE_L,
+                        META_ON},
+                {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyEvent.KEYCODE_N,
+                        META_ON | CTRL_ON},
                 {"Meta + Ctrl + DPAD_DOWN -> Enter desktop mode",
                         new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN},
                         KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
@@ -296,72 +305,14 @@
                         new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
                         KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
                         KeyEvent.KEYCODE_HOME, 0},
-                {"Long press META + ENTER -> Toggle Notification panel",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
-                        LONG_PRESS_HOME_NOTIFICATION_PANEL,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_ENTER,
-                        META_ON},
-                {"Long press META + H -> Toggle Notification panel",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_H, META_ON},
                 {"Long press HOME key -> Launch assistant",
                         new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ASSIST,
                         KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
                         KeyEvent.KEYCODE_HOME, 0},
-                {"Long press META + ENTER -> Launch assistant",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ASSIST,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
-                        KeyEvent.KEYCODE_ENTER, META_ON},
-                {"Long press META + H -> Launch assistant",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H,
-                        META_ON},
                 {"Long press HOME key -> Open App Drawer",
                         new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
                         KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
-                        KeyEvent.KEYCODE_HOME, 0},
-                {"Long press META + ENTER -> Open App Drawer",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
-                        KeyEvent.KEYCODE_ENTER, META_ON},
-                {"Long press META + H -> Open App Drawer",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H},
-                        LONG_PRESS_HOME_ALL_APPS,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
-                        KeyEvent.KEYCODE_H, META_ON}};
-    }
-
-    @Keep
-    private static Object[][] doubleTapOnHomeTestArguments() {
-        // testName, testKeys, doubleTapOnHomeBehavior, expectedKeyGestureType, expectedKey,
-        // expectedModifierState
-        return new Object[][]{
-                {"Double tap HOME -> Open App switcher",
-                        new int[]{KeyEvent.KEYCODE_HOME}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, KeyEvent.KEYCODE_HOME,
-                        0},
-                {"Double tap META + ENTER -> Open App switcher",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
-                        DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
-                        KeyEvent.KEYCODE_ENTER, META_ON},
-                {"Double tap META + H -> Open App switcher",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, KeyEvent.KEYCODE_H,
-                        META_ON}};
-    }
-
-    @Keep
-    private static Object[][] settingsKeyTestArguments() {
-        // testName, testKeys, settingsKeyBehavior, expectedKeyGestureType, expectedKey,
-        // expectedModifierState
-        return new Object[][]{
-                {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
-                        SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL,
-                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_SETTINGS, 0}};
+                        KeyEvent.KEYCODE_HOME, 0}};
     }
 
     @Before
@@ -381,8 +332,18 @@
     }
 
     @Test
-    @Parameters(method = "shortcutTestArguments")
-    public void testShortcut(String testName, int[] testKeys,
+    @Parameters(method = "shortcutTestArgumentsNotMigratedToKeyGestureController")
+    public void testShortcuts_notMigratedToKeyGestureController(String testName,
+            int[] testKeys, @KeyGestureEvent.KeyGestureType int expectedKeyGestureType,
+            int expectedKey, int expectedModifierState) {
+        testShortcutInternal(testName, testKeys, expectedKeyGestureType, expectedKey,
+                expectedModifierState);
+    }
+
+    @Test
+    @Parameters(method = "shortcutTestArgumentsMigratedToKeyGestureController")
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+    public void testShortcuts_migratedToKeyGestureController(String testName, int[] testKeys,
             @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
             int expectedModifierState) {
         testShortcutInternal(testName, testKeys, expectedKeyGestureType, expectedKey,
@@ -402,31 +363,29 @@
     }
 
     @Test
-    @Parameters(method = "doubleTapOnHomeTestArguments")
-    public void testDoubleTapOnHomeBehavior(String testName, int[] testKeys,
-            int doubleTapOnHomeBehavior,
-            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
-            int expectedModifierState) {
-        mPhoneWindowManager.overriderDoubleTapOnHomeBehavior(doubleTapOnHomeBehavior);
-        sendKeyCombination(testKeys, 0 /* duration */);
-        sendKeyCombination(testKeys, 0 /* duration */);
+    public void testDoubleTapOnHomeBehavior_AppSwitchBehavior() {
+        mPhoneWindowManager.overriderDoubleTapOnHomeBehavior(DOUBLE_TAP_HOME_RECENT_SYSTEM_UI);
+        sendKeyCombination(new int[]{KeyEvent.KEYCODE_HOME}, 0 /* duration */);
+        sendKeyCombination(new int[]{KeyEvent.KEYCODE_HOME}, 0 /* duration */);
         mPhoneWindowManager.assertKeyGestureCompleted(
-                new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
-                "Failed while executing " + testName);
+                new int[]{KeyEvent.KEYCODE_HOME}, /* modifierState = */0,
+                KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+                "Failed while executing Double tap HOME -> Open App switcher");
     }
 
     @Test
-    @Parameters(method = "settingsKeyTestArguments")
-    public void testSettingsKey(String testName, int[] testKeys, int settingsKeyBehavior,
-            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
-            int expectedModifierState) {
-        mPhoneWindowManager.overrideSettingsKeyBehavior(settingsKeyBehavior);
-        testShortcutInternal(testName, testKeys, expectedKeyGestureType, expectedKey,
-                expectedModifierState);
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+    public void testSettingsKey_ToggleNotificationBehavior() {
+        mPhoneWindowManager.overrideSettingsKeyBehavior(SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL);
+        testShortcutInternal("SETTINGS key -> Toggle Notification panel",
+                new int[]{KeyEvent.KEYCODE_SETTINGS},
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                KeyEvent.KEYCODE_SETTINGS, 0);
     }
 
     @Test
     @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testBugreportShortcutPress() {
         testShortcutInternal("Meta + Ctrl + Del -> Trigger bug report",
                 new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DEL},
@@ -599,4 +558,145 @@
                 sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH));
         mPhoneWindowManager.assertLaunchSearch();
     }
+
+    @Test
+    public void testKeyGestureScreenshotChord() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
+        mPhoneWindowManager.moveTimeForward(500);
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
+        mPhoneWindowManager.assertTakeScreenshotCalled();
+    }
+
+    @Test
+    public void testKeyGestureScreenshotChordCancelled() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
+        mPhoneWindowManager.assertTakeScreenshotNotCalled();
+    }
+
+    @Test
+    public void testKeyGestureAccessibilityShortcutChord() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
+        mPhoneWindowManager.moveTimeForward(5000);
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
+        mPhoneWindowManager.assertAccessibilityKeychordCalled();
+    }
+
+    @Test
+    public void testKeyGestureAccessibilityShortcutChordCancelled() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD));
+        mPhoneWindowManager.assertAccessibilityKeychordNotCalled();
+    }
+
+    @Test
+    public void testKeyGestureRingerToggleChord() {
+        mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
+        mPhoneWindowManager.moveTimeForward(500);
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
+        mPhoneWindowManager.assertVolumeMute();
+    }
+
+    @Test
+    public void testKeyGestureRingerToggleChordCancelled() {
+        mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
+        mPhoneWindowManager.assertVolumeNotMuted();
+    }
+
+    @Test
+    public void testKeyGestureGlobalAction() {
+        mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
+        mPhoneWindowManager.moveTimeForward(500);
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
+        mPhoneWindowManager.assertShowGlobalActionsCalled();
+    }
+
+    @Test
+    public void testKeyGestureGlobalActionCancelled() {
+        mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
+        mPhoneWindowManager.assertShowGlobalActionsNotCalled();
+    }
+
+    @Test
+    public void testKeyGestureAccessibilityTvShortcutChord() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
+        mPhoneWindowManager.moveTimeForward(5000);
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
+        mPhoneWindowManager.assertAccessibilityKeychordCalled();
+    }
+
+    @Test
+    public void testKeyGestureAccessibilityTvShortcutChordCancelled() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD));
+        mPhoneWindowManager.assertAccessibilityKeychordNotCalled();
+    }
+
+    @Test
+    public void testKeyGestureTvTriggerBugReport() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
+        mPhoneWindowManager.moveTimeForward(1000);
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
+        mPhoneWindowManager.assertBugReportTakenForTv();
+    }
+
+    @Test
+    public void testKeyGestureTvTriggerBugReportCancelled() {
+        Assert.assertTrue(
+                sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
+        Assert.assertTrue(
+                sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
+        mPhoneWindowManager.assertBugReportNotTakenForTv();
+    }
+
+    @Test
+    public void testKeyGestureAccessibilityShortcut() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT));
+        mPhoneWindowManager.assertAccessibilityKeychordCalled();
+    }
+
+    @Test
+    public void testKeyGestureCloseAllDialogs() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS));
+        mPhoneWindowManager.assertCloseAllDialogs();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 43171f8..c186a03 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -119,6 +119,7 @@
      * ALT + TAB to show recent apps.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testAltTab() {
         mPhoneWindowManager.overrideStatusBarManagerInternal();
         sendKeyCombination(new int[]{KEYCODE_ALT_LEFT, KEYCODE_TAB}, 0);
@@ -129,6 +130,7 @@
      * CTRL + SPACE to switch keyboard layout.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testCtrlSpace() {
         sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, /* duration= */ 0,
                 ANY_DISPLAY_ID);
@@ -139,6 +141,7 @@
      * CTRL + SHIFT + SPACE to switch keyboard layout backwards.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testCtrlShiftSpace() {
         sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE},
                 /* duration= */ 0, ANY_DISPLAY_ID);
@@ -149,6 +152,7 @@
      * CTRL + ALT + Z to enable accessibility service.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testCtrlAltZ() {
         sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_ALT_LEFT, KEYCODE_Z}, 0);
         mPhoneWindowManager.assertAccessibilityKeychordCalled();
@@ -158,6 +162,7 @@
      * META + CTRL+ S to take screenshot.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testMetaCtrlS() {
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_S}, 0);
         mPhoneWindowManager.assertTakeScreenshotCalled();
@@ -167,6 +172,7 @@
      * META + N to expand notification panel.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testMetaN() throws RemoteException {
         mPhoneWindowManager.overrideTogglePanel();
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_N}, 0);
@@ -177,6 +183,7 @@
      * META + SLASH to toggle shortcuts menu.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testMetaSlash() {
         mPhoneWindowManager.overrideStatusBarManagerInternal();
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SLASH}, 0);
@@ -187,6 +194,7 @@
      * META  + ALT to toggle Cap Lock.
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testMetaAlt() {
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_ALT_LEFT}, 0);
         mPhoneWindowManager.assertToggleCapsLock();
@@ -196,6 +204,7 @@
      * META + H to go to homescreen
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testMetaH() {
         mPhoneWindowManager.overrideLaunchHome();
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_H}, 0);
@@ -206,6 +215,7 @@
      * META + ENTER to go to homescreen
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testMetaEnter() {
         mPhoneWindowManager.overrideLaunchHome();
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_ENTER}, 0);
@@ -216,6 +226,7 @@
      * Sends a KEYCODE_BRIGHTNESS_DOWN event and validates the brightness is decreased as expected;
      */
     @Test
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testKeyCodeBrightnessDown() {
         float[] currentBrightness = new float[]{0.1f, 0.05f, 0.0f};
         float[] newBrightness = new float[]{0.065738f, 0.0275134f, 0.0f};
@@ -231,9 +242,9 @@
      * Sends a KEYCODE_SCREENSHOT and validates screenshot is taken if flag is enabled
      */
     @Test
+    @EnableFlags(com.android.hardware.input.Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE)
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testTakeScreenshot_flagEnabled() {
-        mSetFlagsRule.enableFlags(com.android.hardware.input.Flags
-                .FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE);
         sendKeyCombination(new int[]{KEYCODE_SCREENSHOT}, 0);
         mPhoneWindowManager.assertTakeScreenshotCalled();
     }
@@ -242,9 +253,9 @@
      * Sends a KEYCODE_SCREENSHOT and validates screenshot is not taken if flag is disabled
      */
     @Test
+    @DisableFlags({com.android.hardware.input.Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE,
+            com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER})
     public void testTakeScreenshot_flagDisabled() {
-        mSetFlagsRule.disableFlags(com.android.hardware.input.Flags
-                .FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE);
         sendKeyCombination(new int[]{KEYCODE_SCREENSHOT}, 0);
         mPhoneWindowManager.assertTakeScreenshotNotCalled();
     }
@@ -254,6 +265,7 @@
      */
     @Test
     @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
     public void testTakeBugReport_flagEnabled() throws RemoteException {
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0);
         mPhoneWindowManager.assertTakeBugreport(true);
@@ -263,7 +275,8 @@
      * META+CTRL+BACKSPACE for taking a bugreport does nothing when the flag is disabledd.
      */
     @Test
-    @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    @DisableFlags({com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT,
+            com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER})
     public void testTakeBugReport_flagDisabled() throws RemoteException {
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0);
         mPhoneWindowManager.assertTakeBugreport(false);
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 50b7db4..9e47a00 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -241,6 +241,13 @@
                         KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
     }
 
+    boolean sendKeyGestureEventCancel(int gestureType) {
+        return mPhoneWindowManager.sendKeyGestureEvent(
+                new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
+                        KeyGestureEvent.ACTION_GESTURE_COMPLETE).setFlags(
+                        KeyGestureEvent.FLAG_CANCELLED).build());
+    }
+
     boolean sendKeyGestureEventComplete(int gestureType, int modifierState) {
         return mPhoneWindowManager.sendKeyGestureEvent(
                 new KeyGestureEvent.Builder().setModifierState(modifierState).setKeyGestureType(
@@ -276,7 +283,7 @@
         if ((actions & ACTION_PASS_TO_USER) != 0) {
             if (0 == mPhoneWindowManager.interceptKeyBeforeDispatching(keyEvent)) {
                 if (!mDispatchedKeyHandler.onKeyDispatched(keyEvent)) {
-                    mPhoneWindowManager.dispatchUnhandledKey(keyEvent);
+                    mPhoneWindowManager.interceptUnhandledKey(keyEvent);
                 }
             }
         }
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 7ea5010..ff8b6d3 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -141,7 +141,8 @@
                     }
 
                     @Override
-                    void onKeyUp(long eventTime, int multiPressCount, int displayId) {
+                    void onKeyUp(long eventTime, int multiPressCount, int displayId, int deviceId,
+                            int metaState) {
                         mKeyUpQueue.add(new KeyUpData(KEYCODE_POWER, multiPressCount));
                     }
                 });
@@ -177,7 +178,8 @@
                     }
 
                     @Override
-                    void onKeyUp(long eventTime, int multiPressCount, int displayId) {
+                    void onKeyUp(long eventTime, int multiPressCount, int displayId, int deviceId,
+                            int metaState) {
                         mKeyUpQueue.add(new KeyUpData(KEYCODE_BACK, multiPressCount));
                     }
 
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 98401b3..1aa9087 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -418,8 +418,8 @@
                 mKeyEventPolicyFlags);
     }
 
-    void dispatchUnhandledKey(KeyEvent event) {
-        mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE);
+    void interceptUnhandledKey(KeyEvent event) {
+        mPhoneWindowManager.interceptUnhandledKey(event, mInputToken);
     }
 
     boolean sendKeyGestureEvent(KeyGestureEvent event) {
@@ -657,16 +657,36 @@
         verify(mPowerManager).userActivity(anyLong(), anyBoolean());
     }
 
+    void assertShowGlobalActionsNotCalled() {
+        mTestLooper.dispatchAll();
+        verify(mGlobalActions, never()).showDialog(anyBoolean(), anyBoolean());
+        verify(mPowerManager, never()).userActivity(anyLong(), anyBoolean());
+    }
+
     void assertVolumeMute() {
         mTestLooper.dispatchAll();
         verify(mAudioManagerInternal).silenceRingerModeInternal(eq("volume_hush"));
     }
 
+    void assertVolumeNotMuted() {
+        mTestLooper.dispatchAll();
+        verify(mAudioManagerInternal, never()).silenceRingerModeInternal(any());
+    }
+
     void assertAccessibilityKeychordCalled() {
         mTestLooper.dispatchAll();
         verify(mAccessibilityShortcutController).performAccessibilityShortcut();
     }
 
+    void assertAccessibilityKeychordNotCalled() {
+        mTestLooper.dispatchAll();
+        verify(mAccessibilityShortcutController, never()).performAccessibilityShortcut();
+    }
+
+    void assertCloseAllDialogs() {
+        verify(mContext).closeSystemDialogs();
+    }
+
     void assertDreamRequest() {
         mTestLooper.dispatchAll();
         verify(mDreamManagerInternal).requestDream();
@@ -809,6 +829,16 @@
 
     }
 
+    void assertBugReportTakenForTv() {
+        mTestLooper.dispatchAll();
+        verify(mPhoneWindowManager).requestBugreportForTv();
+    }
+
+    void assertBugReportNotTakenForTv() {
+        mTestLooper.dispatchAll();
+        verify(mPhoneWindowManager, never()).requestBugreportForTv();
+    }
+
     void assertTogglePanel() throws RemoteException {
         mTestLooper.dispatchAll();
         verify(mPhoneWindowManager.mStatusBarService).togglePanel();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 8227ed9..92205f39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -230,6 +230,10 @@
         mDisplayContent.setIgnoreOrientationRequest(enabled);
     }
 
+    void setTopActivityOrganizedTask() {
+        doReturn(mTaskStack.top()).when(mActivityStack.top()).getOrganizedTask();
+    }
+
     void setTopTaskInMultiWindowMode(boolean inMultiWindowMode) {
         doReturn(inMultiWindowMode).when(mTaskStack.top()).inMultiWindowMode();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index d66c21a..b91a5b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -28,7 +28,7 @@
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
 
 import android.compat.testing.PlatformCompatChangeRule;
 import android.platform.test.annotations.DisableFlags;
@@ -218,7 +218,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testShouldApplyCameraCompatFreeformTreatment_flagIsDisabled_returnsFalse() {
         runTestScenario((robot) -> {
             robot.activity().createActivityWithComponentInNewTask();
@@ -229,7 +229,7 @@
 
     @Test
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
         runTestScenario((robot) -> {
             robot.activity().createActivityWithComponentInNewTask();
@@ -240,7 +240,7 @@
 
     @Test
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testShouldApplyCameraCompatFreeformTreatment_disabledByOverride_returnsFalse() {
         runTestScenario((robot) -> {
             robot.activity().createActivityWithComponentInNewTask();
@@ -250,7 +250,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() {
         runTestScenario((robot) -> {
             robot.activity().createActivityWithComponentInNewTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
index d91b38e..41102d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
@@ -20,7 +20,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -85,7 +85,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_presentWhenEnabledAndDW() {
         runTestScenario((robot) -> {
             robot.allowEnterDesktopMode(/* isAllowed= */ true);
@@ -95,7 +95,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoDW() {
         runTestScenario((robot) -> {
             robot.allowEnterDesktopMode(/* isAllowed= */ false);
@@ -105,7 +105,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoFlag() {
         runTestScenario((robot) -> {
             robot.allowEnterDesktopMode(/* isAllowed= */ true);
@@ -115,7 +115,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoFlagAndNoDW() {
         runTestScenario((robot) -> {
             robot.allowEnterDesktopMode(/* isAllowed= */ false);
@@ -125,7 +125,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraCompatFreeformPolicy_startedWhenEnabledAndDW() {
         runTestScenario((robot) -> {
             robot.allowEnterDesktopMode(/* isAllowed= */ true);
@@ -136,7 +136,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraStateManager_existsWhenCameraCompatFreeformExists() {
         runTestScenario((robot) -> {
             robot.allowEnterDesktopMode(true);
@@ -147,7 +147,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraStateManager_startedWhenCameraCompatFreeformExists() {
         runTestScenario((robot) -> {
             robot.allowEnterDesktopMode(true);
@@ -180,7 +180,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testCameraStateManager_doesNotExistWhenNoPolicyExists() {
         runTestScenario((robot) -> {
             robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
index 21fac9b..d8373c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
@@ -16,10 +16,14 @@
 
 package com.android.server.wm;
 
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static org.mockito.Mockito.when;
 
+import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
+import android.app.TaskInfo;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
@@ -114,6 +118,72 @@
         });
     }
 
+    @Test
+    public void testTopActivityEligibleForUserAspectRatioButton_eligible() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponentInNewTask();
+                a.setIgnoreOrientationRequest(true);
+            });
+            robot.conf().enableUserAppAspectRatioSettings(true);
+
+            robot.checkTaskInfoEligibleForUserAspectRatioButton(true);
+        });
+    }
+
+    @Test
+    public void testTopActivityEligibleForUserAspectRatioButton_disabled_notEligible() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponentInNewTask();
+                a.setIgnoreOrientationRequest(true);
+            });
+            robot.conf().enableUserAppAspectRatioSettings(false);
+
+            robot.checkTaskInfoEligibleForUserAspectRatioButton(false);
+        });
+    }
+
+    @Test
+    public void testTopActivityEligibleForUserAspectRatioButton_inSizeCompatMode_notEligible() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponentInNewTask();
+                a.setIgnoreOrientationRequest(true);
+                a.setTopActivityOrganizedTask();
+                a.setTopActivityInSizeCompatMode(true);
+                a.setTopActivityVisible(true);
+            });
+            robot.conf().enableUserAppAspectRatioSettings(true);
+
+            robot.checkTaskInfoEligibleForUserAspectRatioButton(false);
+        });
+    }
+
+    @Test
+    public void testTopActivityEligibleForUserAspectRatioButton_transparentTop_notEligible() {
+        runTestScenario((robot) -> {
+            robot.transparentActivity((ta) -> {
+                ta.launchTransparentActivityInTask();
+                ta.activity().setIgnoreOrientationRequest(true);
+            });
+            robot.conf().enableUserAppAspectRatioSettings(true);
+
+            robot.checkTaskInfoEligibleForUserAspectRatioButton(false);
+        });
+    }
+
+    @Test
+    public void getTaskInfoPropagatesCameraCompatMode() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity(AppCompatActivityRobot::createActivityWithComponentInNewTask);
+
+            robot.setFreeformCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
+            robot.checkTaskInfoFreeformCameraCompatMode(
+                    CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
+        });
+    }
+
     /**
      * Runs a test scenario providing a Robot.
      */
@@ -125,11 +195,14 @@
     private static class AppCompatUtilsRobotTest extends AppCompatRobotBase {
 
         private final WindowState mWindowState;
+        @NonNull
+        private final AppCompatTransparentActivityRobot mTransparentActivityRobot;
 
         AppCompatUtilsRobotTest(@NonNull WindowManagerService wm,
                 @NonNull ActivityTaskManagerService atm,
                 @NonNull ActivityTaskSupervisor supervisor) {
             super(wm, atm, supervisor);
+            mTransparentActivityRobot = new AppCompatTransparentActivityRobot(activity());
             mWindowState = Mockito.mock(WindowState.class);
         }
 
@@ -139,6 +212,12 @@
             spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy());
         }
 
+        void transparentActivity(@NonNull Consumer<AppCompatTransparentActivityRobot> consumer) {
+            // We always create at least an opaque activity in a Task.
+            activity().createNewTaskWithBaseActivity();
+            consumer.accept(mTransparentActivityRobot);
+        }
+
         void setIsLetterboxedForFixedOrientationAndAspectRatio(
                 boolean forFixedOrientationAndAspectRatio) {
             when(activity().top().mAppCompatController.getAppCompatAspectRatioPolicy()
@@ -155,11 +234,30 @@
             when(mWindowState.isLetterboxedForDisplayCutout()).thenReturn(displayCutout);
         }
 
+        void setFreeformCameraCompatMode(@FreeformCameraCompatMode int mode) {
+            activity().top().mAppCompatController.getAppCompatCameraOverrides()
+                    .setFreeformCameraCompatMode(mode);
+        }
+
         void checkTopActivityLetterboxReason(@NonNull String expected) {
             Assert.assertEquals(expected,
                     AppCompatUtils.getLetterboxReasonString(activity().top(), mWindowState));
         }
 
+        @NonNull
+        TaskInfo getTopTaskInfo() {
+            return activity().top().getTask().getTaskInfo();
+        }
+
+        void checkTaskInfoEligibleForUserAspectRatioButton(boolean eligible) {
+            Assert.assertEquals(eligible, getTopTaskInfo().appCompatTaskInfo
+                    .eligibleForUserAspectRatioButton());
+        }
+
+        void checkTaskInfoFreeformCameraCompatMode(@FreeformCameraCompatMode int mode) {
+            Assert.assertEquals(mode, getTopTaskInfo().appCompatTaskInfo
+                    .cameraCompatTaskInfo.freeformCameraCompatMode);
+        }
     }
 
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index a48813d..dbcef10 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -35,7 +35,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -60,6 +60,7 @@
 import android.graphics.Rect;
 import android.hardware.camera2.CameraManager;
 import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
 import android.view.Surface;
@@ -135,7 +136,6 @@
                 });
 
         mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
-        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
         CameraStateMonitor cameraStateMonitor =
                 new CameraStateMonitor(mDisplayContent, mMockHandler);
         mCameraCompatFreeformPolicy =
@@ -147,6 +147,7 @@
         cameraStateMonitor.startListeningToCameraState();
     }
 
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @Test
     public void testFullscreen_doesNotActivateCameraCompatMode() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
@@ -157,6 +158,7 @@
         assertNotInCameraCompatMode();
     }
 
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @Test
     public void testOrientationUnspecified_doesNotActivateCameraCompatMode() {
         configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
@@ -164,12 +166,14 @@
         assertNotInCameraCompatMode();
     }
 
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @Test
     public void testNoCameraConnection_doesNotActivateCameraCompatMode() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
         assertNotInCameraCompatMode();
     }
 
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @Test
     public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -210,6 +214,7 @@
         assertActivityRefreshRequested(/* refreshRequested */ false);
     }
 
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @Test
     public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -235,6 +240,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
     public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -246,6 +252,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
 
@@ -254,6 +261,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh()
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -268,6 +276,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() throws Exception {
         when(mAppCompatConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
                 .thenReturn(false);
@@ -281,6 +290,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
             throws Exception {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 85cb1bc..5c0d424 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,7 +83,7 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -2820,7 +2820,7 @@
         verify(mWm.mUmInternal, never()).isUserVisible(userId2, displayId);
     }
 
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @Test
     public void cameraCompatFreeformFlagEnabled_cameraCompatFreeformPolicyNotNull() {
         doReturn(true).when(() ->
@@ -2829,7 +2829,7 @@
         assertTrue(createNewDisplay().mAppCompatCameraPolicy.hasCameraCompatFreeformPolicy());
     }
 
-    @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @Test
     public void cameraCompatFreeformFlagNotEnabled_cameraCompatFreeformPolicyIsNull() {
         doReturn(true).when(() ->
@@ -2838,7 +2838,7 @@
         assertFalse(createNewDisplay().mAppCompatCameraPolicy.hasCameraCompatFreeformPolicy());
     }
 
-    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
     @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
     @Test
     public void desktopWindowingFlagNotEnabled_cameraCompatFreeformPolicyIsNull() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index f32a234..6a89178 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -413,15 +413,25 @@
 
     @Test
     public void testUpdateDisplayConfigurationByDecor() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // No configuration update when flag enables.
-            return;
-        }
         doReturn(NO_CUTOUT).when(mDisplayContent).calculateDisplayCutoutForRotation(anyInt());
         final WindowState navbar = createNavBarWithProvidedInsets(mDisplayContent);
         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
         final DisplayInfo di = mDisplayContent.getDisplayInfo();
         final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp;
+        if (Flags.insetsDecoupledConfiguration()) {
+            // No configuration update when flag enables.
+            assertFalse(displayPolicy.updateDecorInsetsInfo());
+            assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
+                    di.logicalHeight, di.logicalWidth).mOverrideConfigInsets.bottom);
+
+            final int barHeight = 2 * NAV_BAR_HEIGHT;
+            navbar.mAttrs.providedInsets[0].setInsetsSize(Insets.of(0, 0, 0, barHeight));
+            assertFalse(displayPolicy.updateDecorInsetsInfo());
+            assertEquals(barHeight, displayPolicy.getDecorInsetsInfo(di.rotation,
+                    di.logicalHeight, di.logicalWidth).mOverrideConfigInsets.bottom);
+            return;
+        }
+
         assertTrue(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo());
         assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
                 di.logicalWidth, di.logicalHeight).mConfigInsets.bottom);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index e8d089c..4570588 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -188,7 +188,7 @@
         assertNull(mProvider.getLeash(target));
 
         // Set the leash to be ready for dispatching.
-        mProvider.mIsLeashReadyForDispatching = true;
+        mProvider.mIsLeashInitialized = true;
         assertNotNull(mProvider.getLeash(target));
 
         // We do have fake control for the fake control target, but that has no leash.
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 1be61c3..66d7963 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -494,6 +494,21 @@
         assertTrue("Result should be empty.", mResult.isEmpty());
     }
 
+    @Test
+    public void testAbortsLoadingWhenUserCleansUpBeforeLoadingFinishes() {
+        mTarget.saveTask(mTestTask);
+        mPersisterQueue.flush();
+
+        final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+                mUserFolderGetter);
+        target.onSystemReady();
+        target.onUnlockUser(TEST_USER_ID);
+        target.onCleanupUser(TEST_USER_ID);
+
+        target.getLaunchParams(mTestTask, null, mResult);
+        assertTrue("Result should be empty.", mResult.isEmpty());
+    }
+
     private static boolean deleteRecursively(File file) {
         boolean result = true;
         if (file.isDirectory()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 8f3d3c5..df17cd1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -70,7 +70,10 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 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.util.ArraySet;
 import android.util.IntArray;
 import android.util.SparseBooleanArray;
@@ -79,9 +82,12 @@
 
 import androidx.test.filters.MediumTest;
 
+import com.android.launcher3.Flags;
 import com.android.server.wm.RecentTasks.Callbacks;
 
 import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -122,6 +128,10 @@
 
     private CallbacksRecorder mCallbacksRecorder;
 
+    @Rule
+    public SetFlagsRule mSetFlagsRule =
+            new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+
     @Before
     public void setUp() throws Exception {
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
@@ -422,6 +432,29 @@
     }
 
     @Test
+    public void testAddTaskCompatibleWindowingMode_withFreeformAndFullscreen_expectRemove() {
+        Task task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .build();
+        doReturn(WINDOWING_MODE_FREEFORM).when(task1).getWindowingMode();
+        mRecentTasks.add(task1);
+        mCallbacksRecorder.clear();
+
+        Task task2 = createTaskBuilder(".Task1")
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .build();
+        assertEquals(WINDOWING_MODE_FULLSCREEN, task2.getWindowingMode());
+        mRecentTasks.add(task2);
+
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertThat(mCallbacksRecorder.mAdded).contains(task2);
+        assertThat(mCallbacksRecorder.mTrimmed).isEmpty();
+        assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
+        assertThat(mCallbacksRecorder.mRemoved).contains(task1);
+    }
+
+    @Test
     public void testAddTaskIncompatibleWindowingMode_expectNoRemove() {
         Task task1 = createTaskBuilder(".Task1")
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
@@ -697,14 +730,31 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
     public void testVisibleTasks_excludedFromRecents() {
+        testVisibleTasks_excludedFromRecents_internal();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+    public void testVisibleTasks_excludedFromRecents_withRefactorFlag() {
+        testVisibleTasks_excludedFromRecents_internal();
+    }
+
+    private void testVisibleTasks_excludedFromRecents_internal() {
         mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
 
-        Task excludedTask1 = createTaskBuilder(".ExcludedTask1")
+        Task invisibleExcludedTask = createTaskBuilder(".ExcludedTask1")
                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .setCreateActivity(true)
                 .build();
-        Task excludedTask2 = createTaskBuilder(".ExcludedTask2")
+        ActivityRecord activityRecord = invisibleExcludedTask.getTopMostActivity();
+        activityRecord.setVisibleRequested(false);
+        activityRecord.setVisible(false);
+
+        Task visibleExcludedTask = createTaskBuilder(".ExcludedTask2")
                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .setCreateActivity(true)
                 .build();
         Task detachedExcludedTask = createTaskBuilder(".DetachedExcludedTask")
                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
@@ -718,18 +768,79 @@
         assertFalse(detachedExcludedTask.isAttached());
 
         mRecentTasks.add(detachedExcludedTask);
-        mRecentTasks.add(excludedTask1);
+        mRecentTasks.add(invisibleExcludedTask);
         mRecentTasks.add(mTasks.get(0));
         mRecentTasks.add(mTasks.get(1));
         mRecentTasks.add(mTasks.get(2));
-        mRecentTasks.add(excludedTask2);
+        mRecentTasks.add(visibleExcludedTask);
 
-        // Except the first-most excluded task, other excluded tasks should be trimmed.
-        triggerTrimAndAssertTrimmed(excludedTask1, detachedExcludedTask);
+        // Excluded tasks should be trimmed, except those with a visible activity.
+        triggerTrimAndAssertTrimmed(invisibleExcludedTask, detachedExcludedTask);
     }
 
     @Test
+    @Ignore("b/342627272")
+    @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+    public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask() {
+        testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+    public void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_withRefactorFlag() {
+        testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal();
+    }
+
+    private void testVisibleTasks_excludedFromRecents_visibleTaskNotFirstTask_internal() {
+        mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
+
+        Task invisibleExcludedTask = createTaskBuilder(".ExcludedTask1")
+                .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .setCreateActivity(true)
+                .build();
+        ActivityRecord activityRecord = invisibleExcludedTask.getTopMostActivity();
+        activityRecord.setVisibleRequested(false);
+        activityRecord.setVisible(false);
+
+        Task visibleExcludedTask = createTaskBuilder(".ExcludedTask2")
+                .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .setCreateActivity(true)
+                .build();
+        Task detachedExcludedTask = createTaskBuilder(".DetachedExcludedTask")
+                .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .build();
+
+        // Move home to front so other task can satisfy the condition in RecentTasks#isTrimmable.
+        mRootWindowContainer.getDefaultTaskDisplayArea().getRootHomeTask().moveToFront("test");
+        // Avoid Task#autoRemoveFromRecents when removing from parent.
+        detachedExcludedTask.setHasBeenVisible(true);
+        detachedExcludedTask.removeImmediately();
+        assertFalse(detachedExcludedTask.isAttached());
+
+        mRecentTasks.add(detachedExcludedTask);
+        mRecentTasks.add(visibleExcludedTask);
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        mRecentTasks.add(mTasks.get(2));
+        mRecentTasks.add(invisibleExcludedTask);
+
+        // Excluded tasks should be trimmed, except those with a visible activity.
+        triggerTrimAndAssertTrimmed(invisibleExcludedTask, detachedExcludedTask);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
     public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() {
+        testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
+    public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible_withRefactorFlag() {
+        testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal();
+    }
+
+    private void testVisibleTasks_excludedFromRecents_firstTaskNotVisible_internal() {
         // Create some set of tasks, some of which are visible and some are not
         Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
                 .setParentTask(mTaskContainer.getRootHomeTask())
@@ -738,11 +849,12 @@
         mRecentTasks.add(homeTask);
         Task excludedTask1 = createTaskBuilder(".ExcludedTask1")
                 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                .setCreateActivity(true)
                 .build();
         excludedTask1.mUserSetupComplete = true;
         mRecentTasks.add(excludedTask1);
 
-        // Expect that the first visible excluded-from-recents task is visible
+        // Expect that the visible excluded-from-recents task is visible
         assertGetRecentTasksOrder(0 /* flags */, excludedTask1);
     }
 
@@ -1439,9 +1551,9 @@
      */
     private void assertGetRecentTasksOrder(int getRecentTaskFlags, Task... expectedTasks) {
         List<RecentTaskInfo> infos = getRecentTasks(getRecentTaskFlags);
-        assertTrue(expectedTasks.length == infos.size());
-        for (int i = 0; i < infos.size(); i++)  {
-            assertTrue(expectedTasks[i].mTaskId == infos.get(i).taskId);
+        assertEquals(expectedTasks.length, infos.size());
+        for (int i = 0; i < infos.size(); i++) {
+            assertEquals(expectedTasks[i].mTaskId, infos.get(i).taskId);
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index ae0c6e5..cf1dcd0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -331,6 +331,8 @@
         final WindowProcessController proc = mSystemServicesTestRule.addProcess(
                 activity.packageName, activity.processName,
                 6789 /* pid */, activity.info.applicationInfo.uid);
+        mAtm.mInternal.preBindApplication(proc, proc.mInfo);
+        assertTrue(proc.registeredForActivityConfigChanges());
         assertFalse(proc.mHasEverAttached);
         try {
             mRootWindowContainer.attachApplication(proc);
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 7bce828..8fa4667 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4063,8 +4063,9 @@
                         .setInsetsSize(Insets.of(0, 0, 0, 150))
         };
         display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
-        assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
-        display.sendNewConfiguration();
+        if (display.getDisplayPolicy().updateDecorInsetsInfo()) {
+            display.sendNewConfiguration();
+        }
 
         final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
@@ -4097,8 +4098,9 @@
                         .setInsetsSize(Insets.of(0, 0, 0, 150))
         };
         display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
-        assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
-        display.sendNewConfiguration();
+        if (display.getDisplayPolicy().updateDecorInsetsInfo()) {
+            display.sendNewConfiguration();
+        }
 
         final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
@@ -4126,8 +4128,9 @@
                         .setInsetsSize(Insets.of(0, 0, 0, 150))
         };
         dc.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
-        assertTrue(dc.getDisplayPolicy().updateDecorInsetsInfo());
-        dc.sendNewConfiguration();
+        if (dc.getDisplayPolicy().updateDecorInsetsInfo()) {
+            dc.sendNewConfiguration();
+        }
 
         final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 7ff2e50..e4512c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -18,7 +18,6 @@
 
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -29,6 +28,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -75,6 +76,7 @@
 import android.app.ActivityOptions;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -97,9 +99,13 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
+import libcore.junit.util.compat.CoreCompatChangeRule;
+
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 import org.xmlpull.v1.XmlPullParser;
@@ -122,6 +128,9 @@
 @RunWith(WindowTestRunner.class)
 public class TaskTests extends WindowTestsBase {
 
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     private static final String TASK_TAG = "task";
 
     private Rect mParentBounds;
@@ -404,6 +413,85 @@
     }
 
     @Test
+    @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
+    public void testIsResizeable_nonResizeable_forceResize_overridesEnabled_Resizeable() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setComponent(
+                        ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
+                .build();
+        task.setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+        // Override should take effect and task should be resizeable.
+        assertTrue(task.getTaskInfo().isResizeable);
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
+    public void testIsResizeable_nonResizeable_forceResize_overridesDisabled_nonResizeable() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setComponent(
+                        ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
+                .build();
+        task.setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+
+        // Disallow resize overrides.
+        task.mAllowForceResizeOverride = false;
+
+        // Override should not take effect and task should be un-resizeable.
+        assertFalse(task.getTaskInfo().isResizeable);
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
+    public void testIsResizeable_resizeable_forceNonResize_overridesEnabled_nonResizeable() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setComponent(
+                        ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
+                .build();
+        task.setResizeMode(RESIZE_MODE_RESIZEABLE);
+
+        // Override should take effect and task should be un-resizeable.
+        assertFalse(task.getTaskInfo().isResizeable);
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
+    public void testIsResizeable_resizeable_forceNonResize_overridesDisabled_Resizeable() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setComponent(
+                        ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
+                .build();
+        task.setResizeMode(RESIZE_MODE_RESIZEABLE);
+
+        // Disallow resize overrides.
+        task.mAllowForceResizeOverride = false;
+
+        // Override should not take effect and task should be resizeable.
+        assertTrue(task.getTaskInfo().isResizeable);
+    }
+
+    @Test
+    @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
+    public void testIsResizeable_systemWideForceResize_compatForceNonResize__Resizeable() {
+        final Task task = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setComponent(
+                        ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
+                .build();
+        task.setResizeMode(RESIZE_MODE_RESIZEABLE);
+
+        // Set system-wide force resizeable override.
+        task.mAtmService.mForceResizableActivities = true;
+
+        // System wide override should tak priority over app compat override so the task should
+        // remain resizeable.
+        assertTrue(task.getTaskInfo().isResizeable);
+    }
+
+    @Test
     public void testResolveNonResizableTaskWindowingMode() {
         // Test with no support non-resizable in multi window regardless the screen size.
         mAtm.mSupportsNonResizableMultiWindow = -1;
@@ -616,50 +704,6 @@
     }
 
     @Test
-    public void testTopActivityEligibleForUserAspectRatioButton() {
-        DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
-        final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
-        final Task task = rootTask.getBottomMostTask();
-        final ActivityRecord root = task.getTopNonFinishingActivity();
-        spyOn(mWm.mAppCompatConfiguration);
-        spyOn(root);
-        spyOn(root.mAppCompatController.getAppCompatAspectRatioOverrides());
-
-        doReturn(true).when(root).fillsParent();
-        doReturn(true).when(
-                root.mAppCompatController.getAppCompatAspectRatioOverrides())
-                    .shouldEnableUserAspectRatioSettings();
-        doReturn(false).when(root).inSizeCompatMode();
-        doReturn(task).when(root).getOrganizedTask();
-
-        // The button should be eligible to be displayed
-        assertTrue(task.getTaskInfo()
-                .appCompatTaskInfo.eligibleForUserAspectRatioButton());
-
-        // When shouldApplyUserMinAspectRatioOverride is disable the button is not enabled
-        doReturn(false).when(
-                root.mAppCompatController.getAppCompatAspectRatioOverrides())
-                    .shouldEnableUserAspectRatioSettings();
-        assertFalse(task.getTaskInfo()
-                .appCompatTaskInfo.eligibleForUserAspectRatioButton());
-        doReturn(true).when(root.mAppCompatController
-                .getAppCompatAspectRatioOverrides()).shouldEnableUserAspectRatioSettings();
-
-        // When in size compat mode the button is not enabled
-        doReturn(true).when(root).inSizeCompatMode();
-        assertFalse(task.getTaskInfo()
-                .appCompatTaskInfo.eligibleForUserAspectRatioButton());
-        doReturn(false).when(root).inSizeCompatMode();
-
-        // When the top activity is transparent, the button is not enabled
-        doReturn(false).when(root).fillsParent();
-        assertFalse(task.getTaskInfo()
-                .appCompatTaskInfo.eligibleForUserAspectRatioButton());
-        doReturn(true).when(root).fillsParent();
-    }
-
-    @Test
     public void testIsTopActivityTranslucent() {
         DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
@@ -2023,17 +2067,6 @@
     }
 
     @Test
-    public void getTaskInfoPropagatesCameraCompatMode() {
-        final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
-        final ActivityRecord activity = task.getTopMostActivity();
-        activity.mAppCompatController.getAppCompatCameraOverrides().setFreeformCameraCompatMode(
-                CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
-
-        assertEquals(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE,
-                task.getTaskInfo().appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode);
-    }
-
-    @Test
     public void testUpdateTaskDescriptionOnReparent() {
         final Task rootTask1 = createTask(mDisplayContent);
         final Task rootTask2 = createTask(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index eebb487..9e9874b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -103,8 +103,8 @@
     }
 
     @Override
-    public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
-        return null;
+    public boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
+        return false;
     }
 
     @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index 4104999..12b7445 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.tools.traces.Utils.busyWaitForDataSourceRegistration;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
@@ -28,7 +30,6 @@
 import static java.io.File.createTempFile;
 import static java.nio.file.Files.createTempDirectory;
 
-import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.Presubmit;
 import android.tools.ScenarioBuilder;
 import android.tools.traces.io.ResultWriter;
@@ -36,9 +37,6 @@
 import android.view.Choreographer;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.junit.After;
 import org.junit.Before;
@@ -46,12 +44,9 @@
 import org.junit.Test;
 import org.mockito.Mockito;
 
-import perfetto.protos.PerfettoConfig.TracingServiceState;
 import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency;
 
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.util.Optional;
 
 /**
  * Test class for {@link WindowTracingPerfetto}.
@@ -74,7 +69,7 @@
         sChoreographer = Mockito.mock(Choreographer.class);
         sWindowTracing = new WindowTracingPerfetto(sWmMock, sChoreographer,
                 new WindowManagerGlobalLock(), TEST_DATA_SOURCE_NAME);
-        waitDataSourceIsAvailable();
+        busyWaitForDataSourceRegistration(TEST_DATA_SOURCE_NAME);
     }
 
     @Before
@@ -156,67 +151,4 @@
 
         mTraceMonitor.stop(writer);
     }
-
-    private static void waitDataSourceIsAvailable() {
-        final int timeoutMs = 10000;
-        final int busyWaitIntervalMs = 100;
-
-        int elapsedMs = 0;
-
-        while (!isDataSourceAvailable()) {
-            try {
-                Thread.sleep(busyWaitIntervalMs);
-                elapsedMs += busyWaitIntervalMs;
-                if (elapsedMs >= timeoutMs) {
-                    throw new RuntimeException("Data source didn't become available."
-                            + " Waited for: " + timeoutMs + " ms");
-                }
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private static boolean isDataSourceAvailable() {
-        byte[] proto = executeShellCommand("perfetto --query-raw");
-
-        try {
-            TracingServiceState state = TracingServiceState.parseFrom(proto);
-
-            Optional<Integer> producerId = Optional.empty();
-
-            for (TracingServiceState.Producer producer : state.getProducersList()) {
-                if (producer.getPid() == android.os.Process.myPid()) {
-                    producerId = Optional.of(producer.getId());
-                    break;
-                }
-            }
-
-            if (producerId.isEmpty()) {
-                return false;
-            }
-
-            for (TracingServiceState.DataSource ds : state.getDataSourcesList()) {
-                if (ds.getDsDescriptor().getName().equals(TEST_DATA_SOURCE_NAME)
-                        && ds.getProducerId() == producerId.get()) {
-                    return true;
-                }
-            }
-        } catch (InvalidProtocolBufferException e) {
-            throw new RuntimeException(e);
-        }
-
-        return false;
-    }
-
-    private static byte[] executeShellCommand(String command) {
-        try {
-            ParcelFileDescriptor fd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                    .executeShellCommand(command);
-            FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(fd);
-            return is.readAllBytes();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 6c1e1a4..1294945 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1027,28 +1027,36 @@
             boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
             if (enabled != mMidiEnabled) {
                 if (enabled) {
+                    boolean midiDeviceFound = false;
                     if (android.hardware.usb.flags.Flags.enableUsbSysfsMidiIdentification()) {
                         try {
                             getMidiCardDevice();
+                            midiDeviceFound = true;
                         } catch (FileNotFoundException e) {
-                            Slog.e(TAG, "could not identify MIDI device", e);
-                            enabled = false;
+                            Slog.w(TAG, "could not identify MIDI device", e);
                         }
-                    } else {
+                    }
+                    // For backward compatibility with older kernels without
+                    // https://lore.kernel.org/r/20240307030922.3573161-1-royluo@google.com
+                    if (!midiDeviceFound) {
                         Scanner scanner = null;
                         try {
                             scanner = new Scanner(new File(MIDI_ALSA_PATH));
                             mMidiCard = scanner.nextInt();
                             mMidiDevice = scanner.nextInt();
+                            midiDeviceFound = true;
                         } catch (FileNotFoundException e) {
                             Slog.e(TAG, "could not open MIDI file", e);
-                            enabled = false;
                         } finally {
                             if (scanner != null) {
                                 scanner.close();
                             }
                         }
                     }
+                    if (!midiDeviceFound) {
+                        Slog.e(TAG, "Failed to enable MIDI function");
+                        enabled = false;
+                    }
                 }
                 mMidiEnabled = enabled;
             }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index dc5f6e9..fb031bd 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -842,7 +842,7 @@
             return;
         }
 
-        model.setRequested(config.allowMultipleTriggers);
+        model.setRequested(config.isAllowMultipleTriggers());
         // TODO: Remove this block if the lower layer supports multiple triggers.
         if (model.isRequested()) {
             updateRecognitionLocked(model, true);
@@ -964,7 +964,7 @@
         RecognitionConfig config = modelData.getRecognitionConfig();
         if (config != null) {
             // Whether we should continue by starting this again.
-            modelData.setRequested(config.allowMultipleTriggers);
+            modelData.setRequested(config.isAllowMultipleTriggers());
         }
         // TODO: Remove this block if the lower layer supports multiple triggers.
         if (modelData.isRequested()) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 862aff9..2bb86bc 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1439,7 +1439,7 @@
                 runOrAddOperation(new Operation(
                         // always execute:
                         () -> {
-                            if (!mRecognitionConfig.allowMultipleTriggers) {
+                            if (!mRecognitionConfig.isAllowMultipleTriggers()) {
                                 // Unregister this remoteService once op is done
                                 synchronized (mCallbacksLock) {
                                     mCallbacks.remove(mPuuid.getUuid());
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index a678147..0f4809c 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -33,6 +33,7 @@
 import android.widget.Toast;
 
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 /**
@@ -283,6 +284,8 @@
         int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
                 ? query.minSdkVersionForFine : query.minSdkVersionForCoarse;
 
+        UserHandle callingUserHandle = UserHandle.getUserHandleForUid(query.callingUid);
+
         // If the app fails for some reason, see if it should be allowed to proceed.
         if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) {
             String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
@@ -291,7 +294,8 @@
                     + query.method;
             logError(context, query, errorMsg);
             return null;
-        } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) {
+        } else if (!isAppAtLeastSdkVersion(context, callingUserHandle, query.callingPackage,
+                minSdkVersion)) {
             String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
                     + " because it doesn't target API " + minSdkVersion + " yet."
                     + " Please fix this app. Called from " + query.method;
@@ -420,11 +424,19 @@
         }
     }
 
-    private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) {
+    private static boolean isAppAtLeastSdkVersion(Context context,
+            @NonNull UserHandle callingUserHandle, String pkgName, int sdkVersion) {
         try {
-            if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
-                    >= sdkVersion) {
-                return true;
+            if (Flags.hsumPackageManager()) {
+                if (context.getPackageManager().getApplicationInfoAsUser(
+                        pkgName, 0, callingUserHandle).targetSdkVersion >= sdkVersion) {
+                    return true;
+                }
+            } else {
+                if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
+                        >= sdkVersion) {
+                    return true;
+                }
             }
         } catch (PackageManager.NameNotFoundException e) {
             // In case of exception, assume known app (more strict checking)
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index e20e4d2..e42b41f 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -159,7 +159,7 @@
 
         /**
          * @return the conditional barring factor as a percentage 0-100, which is the probability of
-         *         a random device being barred for the service type.
+         *         a random device being allowed for a conditionally barred service.
          */
         public int getConditionalBarringFactor() {
             return mConditionalBarringFactor;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2ef0573..47f6764 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9763,7 +9763,7 @@
      * users to switch to using satellite emergency messaging.</li>
      * </ul>
      * <p>
-     * The default value is 300 seconds.
+     * The default value is 180 seconds.
      */
     @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
     public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT =
@@ -11257,7 +11257,7 @@
                 KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
                 PersistableBundle.EMPTY);
         sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
-        sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 300);
+        sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 180);
         sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
                 // Boundaries: [-140 dBm, -44 dBm]
                 new int[]{
diff --git a/tests/FlickerTests/ActivityEmbedding/OWNERS b/tests/FlickerTests/ActivityEmbedding/OWNERS
new file mode 100644
index 0000000..981b316
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/OWNERS
@@ -0,0 +1 @@
+# Bug component: 1168918
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 3f6a0bf..c77413b 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -20,6 +20,7 @@
 import android.graphics.Insets
 import android.graphics.Rect
 import android.graphics.Region
+import android.os.SystemClock
 import android.platform.uiautomator_helpers.DeviceHelpers
 import android.tools.device.apphelpers.IStandardAppHelper
 import android.tools.helpers.SYSTEMUI_PACKAGE
@@ -27,11 +28,14 @@
 import android.tools.traces.wm.WindowingMode
 import android.view.WindowInsets
 import android.view.WindowManager
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.BySelector
 import androidx.test.uiautomator.UiDevice
 import androidx.test.uiautomator.UiObject2
 import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod.TOUCH
+import com.android.window.flags.Flags
 import java.time.Duration
 
 /**
@@ -69,13 +73,22 @@
     fun enterDesktopWithDrag(
         wmHelper: WindowManagerStateHelper,
         device: UiDevice,
+        motionEventHelper: MotionEventHelper = MotionEventHelper(getInstrumentation(), TOUCH)
     ) {
         innerHelper.launchViaIntent(wmHelper)
-        dragToDesktop(wmHelper, device)
+        dragToDesktop(
+            wmHelper = wmHelper,
+            device = device,
+            motionEventHelper = motionEventHelper
+        )
         waitForAppToMoveToDesktop(wmHelper)
     }
 
-    private fun dragToDesktop(wmHelper: WindowManagerStateHelper, device: UiDevice) {
+    private fun dragToDesktop(
+        wmHelper: WindowManagerStateHelper,
+        device: UiDevice,
+        motionEventHelper: MotionEventHelper
+    ) {
         val windowRect = wmHelper.getWindowRegion(innerHelper).bounds
         val startX = windowRect.centerX()
 
@@ -88,7 +101,17 @@
         val endY = displayRect.centerY() / 2
 
         // drag the window to move to desktop
-        device.drag(startX, startY, startX, endY, 100)
+        if (motionEventHelper.inputMethod == TOUCH
+            && Flags.enableHoldToDragAppHandle()) {
+            // Touch requires hold-to-drag.
+            val downTime = SystemClock.uptimeMillis()
+            motionEventHelper.actionDown(startX, startY, time = downTime)
+            SystemClock.sleep(100L) // hold for 100ns before starting the move.
+            motionEventHelper.actionMove(startX, startY, startX, endY, 100, downTime = downTime)
+            motionEventHelper.actionUp(startX, endY, downTime = downTime)
+        } else {
+            device.drag(startX, startY, startX, endY, 100)
+        }
     }
 
     private fun getMaximizeButtonForTheApp(caption: UiObject2?): UiObject2 {
@@ -220,9 +243,10 @@
         val endY = startY + verticalChange
         val endX = startX + horizontalChange
 
-        motionEvent.actionDown(startX, startY)
-        motionEvent.actionMove(startX, startY, endX, endY, /* steps= */100)
-        motionEvent.actionUp(endX, endY)
+        val downTime = SystemClock.uptimeMillis()
+        motionEvent.actionDown(startX, startY, time = downTime)
+        motionEvent.actionMove(startX, startY, endX, endY, /* steps= */100, downTime = downTime)
+        motionEvent.actionUp(endX, endY, downTime = downTime)
         wmHelper
             .StateSyncBuilder()
             .withAppTransitionIdle()
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
index 0835398..86a0b0f 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
@@ -21,6 +21,7 @@
 import android.view.ContentInfo.Source
 import android.view.InputDevice.SOURCE_MOUSE
 import android.view.InputDevice.SOURCE_STYLUS
+import android.view.InputDevice.SOURCE_TOUCHSCREEN
 import android.view.MotionEvent
 import android.view.MotionEvent.ACTION_DOWN
 import android.view.MotionEvent.ACTION_MOVE
@@ -36,23 +37,24 @@
  */
 class MotionEventHelper(
     private val instr: Instrumentation,
-    private val inputMethod: InputMethod
+    val inputMethod: InputMethod
 ) {
     enum class InputMethod(@ToolType val toolType: Int, @Source val source: Int) {
         STYLUS(TOOL_TYPE_STYLUS, SOURCE_STYLUS),
         MOUSE(TOOL_TYPE_MOUSE, SOURCE_MOUSE),
-        TOUCHPAD(TOOL_TYPE_FINGER, SOURCE_MOUSE)
+        TOUCHPAD(TOOL_TYPE_FINGER, SOURCE_MOUSE),
+        TOUCH(TOOL_TYPE_FINGER, SOURCE_TOUCHSCREEN)
     }
 
-    fun actionDown(x: Int, y: Int) {
-        injectMotionEvent(ACTION_DOWN, x, y)
+    fun actionDown(x: Int, y: Int, time: Long = SystemClock.uptimeMillis()) {
+        injectMotionEvent(ACTION_DOWN, x, y, downTime = time, eventTime = time)
     }
 
-    fun actionUp(x: Int, y: Int) {
-        injectMotionEvent(ACTION_UP, x, y)
+    fun actionUp(x: Int, y: Int, downTime: Long) {
+        injectMotionEvent(ACTION_UP, x, y, downTime = downTime)
     }
 
-    fun actionMove(startX: Int, startY: Int, endX: Int, endY: Int, steps: Int) {
+    fun actionMove(startX: Int, startY: Int, endX: Int, endY: Int, steps: Int, downTime: Long) {
         val incrementX = (endX - startX).toFloat() / (steps - 1)
         val incrementY = (endY - startY).toFloat() / (steps - 1)
 
@@ -61,14 +63,19 @@
             val x = startX + incrementX * i
             val y = startY + incrementY * i
 
-            val moveEvent = getMotionEvent(time, time, ACTION_MOVE, x, y)
+            val moveEvent = getMotionEvent(downTime, time, ACTION_MOVE, x, y)
             injectMotionEvent(moveEvent)
         }
     }
 
-    private fun injectMotionEvent(action: Int, x: Int, y: Int): MotionEvent {
-        val eventTime = SystemClock.uptimeMillis()
-        val event = getMotionEvent(eventTime, eventTime, action, x.toFloat(), y.toFloat())
+    private fun injectMotionEvent(
+        action: Int,
+        x: Int,
+        y: Int,
+        downTime: Long = SystemClock.uptimeMillis(),
+        eventTime: Long = SystemClock.uptimeMillis()
+    ): MotionEvent {
+        val event = getMotionEvent(downTime, eventTime, action, x.toFloat(), y.toFloat())
         injectMotionEvent(event)
         return event
     }
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index ba36007..4ae06a4 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -18,6 +18,8 @@
 
 import android.content.Context
 import android.content.ContextWrapper
+import android.content.pm.PackageManager
+import android.content.res.Resources
 import android.hardware.input.IInputManager
 import android.hardware.input.AidlKeyGestureEvent
 import android.hardware.input.IKeyGestureEventListener
@@ -25,15 +27,20 @@
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyGestureEvent
-import android.hardware.input.KeyGestureEvent.KeyGestureType
 import android.os.IBinder
 import android.os.Process
+import android.os.SystemClock
+import android.os.SystemProperties
 import android.os.test.TestLooper
+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.InputDevice
-import android.view.KeyCharacterMap
 import android.view.KeyEvent
+import android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE
 import androidx.test.core.app.ApplicationProvider
+import com.android.internal.R
 import com.android.internal.annotations.Keep
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
@@ -78,32 +85,60 @@
             KeyEvent.KEYCODE_META_LEFT to (KeyEvent.META_META_LEFT_ON or KeyEvent.META_META_ON),
             KeyEvent.KEYCODE_META_RIGHT to (KeyEvent.META_META_RIGHT_ON or KeyEvent.META_META_ON),
         )
+        const val SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH = 0
+        const val SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY = 1
+        const val SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0
+        const val SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL = 1
+        const val SETTINGS_KEY_BEHAVIOR_NOTHING = 2
     }
 
     @JvmField
     @Rule
     val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
-        .mockStatic(FrameworkStatsLog::class.java).build()!!
+        .mockStatic(FrameworkStatsLog::class.java)
+        .mockStatic(SystemProperties::class.java).build()!!
+
+    @JvmField
+    @Rule
+    val rule = SetFlagsRule()
 
     @Mock
     private lateinit var iInputManager: IInputManager
 
+    @Mock
+    private lateinit var resources: Resources
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
     private var currentPid = 0
-    private lateinit var keyGestureController: KeyGestureController
     private lateinit var context: Context
     private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
     private lateinit var testLooper: TestLooper
     private var events = mutableListOf<KeyGestureEvent>()
-    private var handleEvents = mutableListOf<KeyGestureEvent>()
 
     @Before
     fun setup() {
         context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+        Mockito.`when`(context.resources).thenReturn(resources)
         inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
         setupInputDevices()
+        setupBehaviors()
         testLooper = TestLooper()
         currentPid = Process.myPid()
-        keyGestureController = KeyGestureController(context, testLooper.looper)
+    }
+
+    private fun setupBehaviors() {
+        Mockito.`when`(
+            resources.getBoolean(
+                com.android.internal.R.bool.config_enableScreenshotChord
+            )
+        ).thenReturn(true)
+        Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH))
+            .thenReturn(true)
+        Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+            .thenReturn(true)
+        Mockito.`when`(context.packageManager).thenReturn(packageManager)
     }
 
     private fun setupInputDevices() {
@@ -116,19 +151,22 @@
         Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
     }
 
-    private fun notifyHomeGestureCompleted() {
-        keyGestureController.notifyKeyGestureCompleted(DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H),
+    private fun notifyHomeGestureCompleted(keyGestureController: KeyGestureController) {
+        keyGestureController.notifyKeyGestureCompleted(
+            DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H),
             KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
-            KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+            KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+        )
     }
 
     @Test
     fun testKeyGestureEvent_registerUnregisterListener() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
         val listener = KeyGestureEventListener()
 
         // Register key gesture event listener
         keyGestureController.registerKeyGestureEventListener(listener, 0)
-        notifyHomeGestureCompleted()
+        notifyHomeGestureCompleted(keyGestureController)
         testLooper.dispatchAll()
         assertEquals(
             "Listener should get callbacks on key gesture event completed",
@@ -144,7 +182,7 @@
         // Unregister listener
         events.clear()
         keyGestureController.unregisterKeyGestureEventListener(listener, 0)
-        notifyHomeGestureCompleted()
+        notifyHomeGestureCompleted(keyGestureController)
         testLooper.dispatchAll()
         assertEquals(
             "Listener should not get callback after being unregistered",
@@ -155,20 +193,22 @@
 
     @Test
     fun testKeyGestureEvent_multipleGestureHandlers() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+
         // Set up two callbacks.
         var callbackCount1 = 0
         var callbackCount2 = 0
         var selfCallback = 0
         val externalHandler1 = KeyGestureHandler { _, _ ->
-            callbackCount1++;
+            callbackCount1++
             true
         }
         val externalHandler2 = KeyGestureHandler { _, _ ->
-            callbackCount2++;
+            callbackCount2++
             true
         }
         val selfHandler = KeyGestureHandler { _, _ ->
-            selfCallback++;
+            selfCallback++
             false
         }
 
@@ -406,6 +446,14 @@
                 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,
+                intArrayOf(KeyEvent.KEYCODE_SLASH),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
                 "BRIGHTNESS_UP -> Brightness Up",
                 intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP),
                 KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
@@ -528,12 +576,314 @@
                     KeyGestureEvent.ACTION_GESTURE_COMPLETE
                 )
             ),
+            TestData(
+                "CTRL + SPACE -> Switch Language Forward",
+                intArrayOf(KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_SPACE),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                intArrayOf(KeyEvent.KEYCODE_SPACE),
+                KeyEvent.META_CTRL_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "CTRL + SHIFT + SPACE -> Switch Language Backward",
+                intArrayOf(
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_SHIFT_LEFT,
+                    KeyEvent.KEYCODE_SPACE
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                intArrayOf(KeyEvent.KEYCODE_SPACE),
+                KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "CTRL + ALT + Z -> Accessibility Shortcut",
+                intArrayOf(
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_Z
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
+                intArrayOf(KeyEvent.KEYCODE_Z),
+                KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "SYSRQ -> Take screenshot",
+                intArrayOf(KeyEvent.KEYCODE_SYSRQ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+                intArrayOf(KeyEvent.KEYCODE_SYSRQ),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
+            TestData(
+                "ESC -> Close All Dialogs",
+                intArrayOf(KeyEvent.KEYCODE_ESCAPE),
+                KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
+                intArrayOf(KeyEvent.KEYCODE_ESCAPE),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
         )
     }
 
     @Test
     @Parameters(method = "keyGestureEventHandlerTestArguments")
     fun testKeyGestures(test: TestData) {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(keyGestureController, test)
+    }
+
+    @Test
+    fun testKeycodesFullyConsumed_irrespectiveOfHandlers() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        val testKeys = intArrayOf(
+            KeyEvent.KEYCODE_RECENT_APPS,
+            KeyEvent.KEYCODE_APP_SWITCH,
+            KeyEvent.KEYCODE_BRIGHTNESS_UP,
+            KeyEvent.KEYCODE_BRIGHTNESS_DOWN,
+            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN,
+            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP,
+            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE,
+            KeyEvent.KEYCODE_ALL_APPS,
+            KeyEvent.KEYCODE_NOTIFICATION,
+            KeyEvent.KEYCODE_SETTINGS,
+            KeyEvent.KEYCODE_LANGUAGE_SWITCH,
+            KeyEvent.KEYCODE_SCREENSHOT,
+            KeyEvent.KEYCODE_META_LEFT,
+            KeyEvent.KEYCODE_META_RIGHT,
+            KeyEvent.KEYCODE_ASSIST,
+            KeyEvent.KEYCODE_VOICE_ASSIST,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY,
+            KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL,
+        )
+
+        val handler = KeyGestureHandler { _, _ -> false }
+        keyGestureController.registerKeyGestureHandler(handler, 0)
+
+        for (key in testKeys) {
+            sendKeys(keyGestureController, intArrayOf(key), assertNotSentToApps = true)
+        }
+    }
+
+    @Test
+    fun testSearchKeyGestures_defaultSearch() {
+        Mockito.`when`(resources.getInteger(R.integer.config_searchKeyBehavior))
+            .thenReturn(SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH)
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureNotProduced(
+            keyGestureController,
+            "SEARCH -> Default Search",
+            intArrayOf(KeyEvent.KEYCODE_SEARCH),
+        )
+    }
+
+    @Test
+    fun testSearchKeyGestures_searchActivity() {
+        Mockito.`when`(resources.getInteger(R.integer.config_searchKeyBehavior))
+            .thenReturn(SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY)
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "SEARCH -> Launch Search Activity",
+                intArrayOf(KeyEvent.KEYCODE_SEARCH),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+                intArrayOf(KeyEvent.KEYCODE_SEARCH),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    fun testSettingKeyGestures_doNothing() {
+        Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior))
+            .thenReturn(SETTINGS_KEY_BEHAVIOR_NOTHING)
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureNotProduced(
+            keyGestureController,
+            "SETTINGS -> Do Nothing",
+            intArrayOf(KeyEvent.KEYCODE_SETTINGS),
+        )
+    }
+
+    @Test
+    fun testSettingKeyGestures_settingsActivity() {
+        Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior))
+            .thenReturn(SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY)
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "SETTINGS -> Launch Settings Activity",
+                intArrayOf(KeyEvent.KEYCODE_SETTINGS),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+                intArrayOf(KeyEvent.KEYCODE_SETTINGS),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    fun testSettingKeyGestures_notificationPanel() {
+        Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior))
+            .thenReturn(SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL)
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "SETTINGS -> Toggle Notification Panel",
+                intArrayOf(KeyEvent.KEYCODE_SETTINGS),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                intArrayOf(KeyEvent.KEYCODE_SETTINGS),
+                0,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    fun testTriggerBugReport() {
+        Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1")
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "META + CTRL + DEL -> Trigger Bug Report",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_DEL
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+                intArrayOf(KeyEvent.KEYCODE_DEL),
+                KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    fun testTriggerBugReport_flagDisabled() {
+        Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1")
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "META + CTRL + DEL -> Not Trigger Bug Report (Fallback to BACK)",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_DEL
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+                intArrayOf(KeyEvent.KEYCODE_DEL),
+                KeyEvent.META_META_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    fun testCapsLockPressNotified() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        val listener = KeyGestureEventListener()
+
+        keyGestureController.registerKeyGestureEventListener(listener, 0)
+        sendKeys(keyGestureController, intArrayOf(KeyEvent.KEYCODE_CAPS_LOCK))
+        testLooper.dispatchAll()
+        assertEquals(
+            "Listener should get callbacks on key gesture event completed",
+            1,
+            events.size
+        )
+        assertEquals(
+            "Listener should get callback for Toggle Caps Lock key gesture complete event",
+            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+            events[0].keyGestureType
+        )
+    }
+
+    @Keep
+    private fun keyGestureEventHandlerTestArguments_forKeyCombinations(): Array<TestData> {
+        return arrayOf(
+            TestData(
+                "VOLUME_DOWN + POWER -> Screenshot Chord",
+                intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER),
+                KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
+                intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER),
+                0,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_START,
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+            TestData(
+                "POWER + STEM_PRIMARY -> Screenshot Chord",
+                intArrayOf(KeyEvent.KEYCODE_POWER, KeyEvent.KEYCODE_STEM_PRIMARY),
+                KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
+                intArrayOf(KeyEvent.KEYCODE_POWER, KeyEvent.KEYCODE_STEM_PRIMARY),
+                0,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_START,
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+            TestData(
+                "VOLUME_DOWN + VOLUME_UP -> Accessibility Chord",
+                intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP),
+                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
+                intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP),
+                0,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_START,
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+            TestData(
+                "BACK + DPAD_DOWN -> TV Accessibility Chord",
+                intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
+                intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
+                0,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_START,
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+            TestData(
+                "BACK + DPAD_CENTER -> TV Trigger Bug Report",
+                intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
+                intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER),
+                0,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_START,
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+        )
+    }
+
+    @Test
+    @Parameters(method = "keyGestureEventHandlerTestArguments_forKeyCombinations")
+    @EnableFlags(
+        com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER,
+        com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_PRESS_GESTURES
+    )
+    fun testKeyCombinationGestures(test: TestData) {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(keyGestureController, test)
+    }
+
+    private fun testKeyGestureInternal(keyGestureController: KeyGestureController, test: TestData) {
+        var handleEvents = mutableListOf<KeyGestureEvent>()
         val handler = KeyGestureHandler { event, _ ->
             handleEvents.add(KeyGestureEvent(event))
             true
@@ -541,7 +891,7 @@
         keyGestureController.registerKeyGestureHandler(handler, 0)
         handleEvents.clear()
 
-        sendKeys(test.keys, /* assertAllConsumed = */ false)
+        sendKeys(keyGestureController, test.keys)
 
         assertEquals(
             "Test: $test doesn't produce correct number of key gesture events",
@@ -575,55 +925,37 @@
         keyGestureController.unregisterKeyGestureHandler(handler, 0)
     }
 
-    @Test
-    fun testKeycodesFullyConsumed_irrespectiveOfHandlers() {
-        val testKeys = intArrayOf(
-            KeyEvent.KEYCODE_RECENT_APPS,
-            KeyEvent.KEYCODE_APP_SWITCH,
-            KeyEvent.KEYCODE_BRIGHTNESS_UP,
-            KeyEvent.KEYCODE_BRIGHTNESS_DOWN,
-            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN,
-            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP,
-            KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE,
-            KeyEvent.KEYCODE_ALL_APPS,
-            KeyEvent.KEYCODE_NOTIFICATION,
-            KeyEvent.KEYCODE_SETTINGS,
-            KeyEvent.KEYCODE_LANGUAGE_SWITCH,
-            KeyEvent.KEYCODE_SCREENSHOT,
-            KeyEvent.KEYCODE_META_LEFT,
-            KeyEvent.KEYCODE_META_RIGHT,
-            KeyEvent.KEYCODE_ASSIST,
-            KeyEvent.KEYCODE_VOICE_ASSIST,
-            KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY,
-            KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY,
-            KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY,
-            KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL,
-        )
-
-        val handler = KeyGestureHandler { _, _ -> false }
-        keyGestureController.registerKeyGestureHandler(handler, 0)
-
-        for (key in testKeys) {
-            sendKeys(intArrayOf(key), /* assertAllConsumed = */ true)
+    private fun testKeyGestureNotProduced(
+        keyGestureController: KeyGestureController,
+        testName: String,
+        testKeys: IntArray
+    ) {
+        var handleEvents = mutableListOf<KeyGestureEvent>()
+        val handler = KeyGestureHandler { event, _ ->
+            handleEvents.add(KeyGestureEvent(event))
+            true
         }
+        keyGestureController.registerKeyGestureHandler(handler, 0)
+        handleEvents.clear()
+
+        sendKeys(keyGestureController, testKeys)
+        assertEquals("Test: $testName should not produce Key gesture", 0, handleEvents.size)
     }
 
-    private fun sendKeys(testKeys: IntArray, assertAllConsumed: Boolean) {
+    private fun sendKeys(
+        keyGestureController: KeyGestureController,
+        testKeys: IntArray,
+        assertNotSentToApps: Boolean = false
+    ) {
         var metaState = 0
+        val now = SystemClock.uptimeMillis()
         for (key in testKeys) {
             val downEvent = KeyEvent(
-                /* downTime = */0, /* eventTime = */ 0, KeyEvent.ACTION_DOWN, key,
-                0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
-                0 /*flags*/, InputDevice.SOURCE_KEYBOARD
+                now, now, KeyEvent.ACTION_DOWN, key, 0 /*repeat*/, metaState,
+                DEVICE_ID, 0 /*scancode*/, 0 /*flags*/,
+                InputDevice.SOURCE_KEYBOARD
             )
-            val consumed =
-                keyGestureController.interceptKeyBeforeDispatching(null, downEvent, 0) == -1L
-            if (assertAllConsumed) {
-                assertTrue(
-                    "interceptKeyBeforeDispatching should consume all events $downEvent",
-                    consumed
-                )
-            }
+            interceptKey(keyGestureController, downEvent, assertNotSentToApps)
             metaState = metaState or MODIFIER.getOrDefault(key, 0)
 
             downEvent.recycle()
@@ -632,24 +964,39 @@
 
         for (key in testKeys.reversed()) {
             val upEvent = KeyEvent(
-                /* downTime = */0, /* eventTime = */ 0, KeyEvent.ACTION_UP, key,
-                0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
-                0 /*flags*/, InputDevice.SOURCE_KEYBOARD
+                now, now, KeyEvent.ACTION_UP, key, 0 /*repeat*/, metaState,
+                DEVICE_ID, 0 /*scancode*/, 0 /*flags*/,
+                InputDevice.SOURCE_KEYBOARD
             )
-            val consumed =
-                keyGestureController.interceptKeyBeforeDispatching(null, upEvent, 0) == -1L
-            if (assertAllConsumed) {
-                assertTrue(
-                    "interceptKeyBeforeDispatching should consume all events $upEvent",
-                    consumed
-                )
-            }
+            interceptKey(keyGestureController, upEvent, assertNotSentToApps)
+            metaState = metaState and MODIFIER.getOrDefault(key, 0).inv()
 
             upEvent.recycle()
             testLooper.dispatchAll()
         }
     }
 
+    private fun interceptKey(
+        keyGestureController: KeyGestureController,
+        event: KeyEvent,
+        assertNotSentToApps: Boolean
+    ) {
+        keyGestureController.interceptKeyBeforeQueueing(event, FLAG_INTERACTIVE)
+        testLooper.dispatchAll()
+
+        val consumed =
+            keyGestureController.interceptKeyBeforeDispatching(null, event, 0) == -1L
+        if (assertNotSentToApps) {
+            assertTrue(
+                "interceptKeyBeforeDispatching should consume all events $event",
+                consumed
+            )
+        }
+        if (!consumed) {
+            keyGestureController.interceptUnhandledKey(event, null)
+        }
+    }
+
     inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() {
         override fun onKeyGestureEvent(event: AidlKeyGestureEvent) {
             events.add(KeyGestureEvent(event))
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
index b3a998e..60fa52f 100644
--- a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
@@ -57,6 +57,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.function.Consumer;
+
 /**
  * Build/Install/Run:
  * atest TouchpadDebugViewTest
@@ -99,10 +101,12 @@
 
         when(mInputManager.getInputDevice(TOUCHPAD_DEVICE_ID)).thenReturn(inputDevice);
 
+        Consumer<Integer> touchpadSwitchHandler = id -> {};
+
         mTouchpadDebugView = new TouchpadDebugView(mTestableContext, TOUCHPAD_DEVICE_ID,
                 new TouchpadHardwareProperties.Builder(0f, 0f, 500f,
                         500f, 45f, 47f, -4f, 5f, (short) 10, true,
-                        true).build());
+                        true).build(), touchpadSwitchHandler);
 
         mTouchpadDebugView.measure(
                 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
@@ -321,26 +325,30 @@
                 new TouchpadHardwareState(0, 1 /* buttonsDown */, 0, 0,
                         new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
 
-        assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
+        assertEquals(((ColorDrawable) child.getBackground()).getColor(),
+                Color.parseColor("#769763"));
 
         mTouchpadDebugView.updateHardwareState(
                 new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
                         new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
 
-        assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.RED);
+        assertEquals(((ColorDrawable) child.getBackground()).getColor(),
+                Color.parseColor("#5455A9"));
 
         mTouchpadDebugView.updateHardwareState(
                 new TouchpadHardwareState(0, 1 /* buttonsDown */, 0, 0,
                         new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID);
 
-        assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
+        assertEquals(((ColorDrawable) child.getBackground()).getColor(),
+                Color.parseColor("#769763"));
 
         // Color should not change because hardware state of a different touchpad
         mTouchpadDebugView.updateHardwareState(
                 new TouchpadHardwareState(0, 0 /* buttonsDown */, 0, 0,
                         new TouchpadFingerState[0]), TOUCHPAD_DEVICE_ID + 1);
 
-        assertEquals(((ColorDrawable) child.getBackground()).getColor(), Color.BLUE);
+        assertEquals(((ColorDrawable) child.getBackground()).getColor(),
+                Color.parseColor("#769763"));
     }
 
     @Test
@@ -394,4 +402,73 @@
         // Verify that no updateViewLayout is called (as expected for a two-finger drag gesture).
         verify(mWindowManager, times(0)).updateViewLayout(any(), any());
     }
-}
\ No newline at end of file
+
+    @Test
+    public void testPinchDrag() {
+        float offsetY = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() + 10;
+
+        MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_MOUSE)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(40f)
+                )
+                .classification(MotionEvent.CLASSIFICATION_PINCH)
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+        MotionEvent pointerDown = new MotionEventBuilder(MotionEvent.ACTION_POINTER_DOWN,
+                SOURCE_MOUSE)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(40f)
+                )
+                .pointer(new PointerBuilder(1, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(45f)
+                )
+                .classification(MotionEvent.CLASSIFICATION_PINCH)
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(pointerDown);
+
+        // Simulate ACTION_MOVE event (both fingers moving apart).
+        MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_MOUSE)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(40f - offsetY)
+                )
+                .rawXCursorPosition(mWindowLayoutParams.x + 10f)
+                .rawYCursorPosition(mWindowLayoutParams.y + 10f)
+                .pointer(new PointerBuilder(1, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(45f + offsetY)
+                )
+                .classification(MotionEvent.CLASSIFICATION_PINCH)
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+        MotionEvent pointerUp = new MotionEventBuilder(MotionEvent.ACTION_POINTER_UP, SOURCE_MOUSE)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(40f - offsetY)
+                )
+                .pointer(new PointerBuilder(1, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(45f + offsetY)
+                )
+                .classification(MotionEvent.CLASSIFICATION_PINCH)
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(pointerUp);
+
+        MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_MOUSE)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(40f - offsetY)
+                )
+                .classification(MotionEvent.CLASSIFICATION_PINCH)
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+        // Verify that no updateViewLayout is called (as expected for a two-finger drag gesture).
+        verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+    }
+}
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index 3e58517..9f35c7b 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -32,6 +32,27 @@
     test_suites: ["device-tests"],
 }
 
+// Run just ApplicationSharedMemoryTest with ABI override for 32 bits.
+// This is to test that on systems that support multi-ABI,
+// ApplicationSharedMemory works in app processes launched with a different ABI
+// than that of the system processes.
+android_test {
+    name: "ApplicationSharedMemoryTest32",
+    team: "trendy_team_system_performance",
+    srcs: ["src/com/android/internal/os/ApplicationSharedMemoryTest.java"],
+    libs: ["android.test.runner.stubs.system"],
+    static_libs: [
+        "junit",
+        "androidx.test.rules",
+        "platform-test-annotations",
+    ],
+    manifest: "ApplicationSharedMemoryTest32/AndroidManifest.xml",
+    test_config: "ApplicationSharedMemoryTest32/AndroidTest.xml",
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
+
 android_ravenwood_test {
     name: "InternalTestsRavenwood",
     static_libs: [
@@ -45,3 +66,9 @@
     ],
     auto_gen_config: true,
 }
+
+java_test_helper_library {
+    name: "ApplicationSharedMemoryTestRule",
+    srcs: ["src/com/android/internal/os/ApplicationSharedMemoryTestRule.java"],
+    static_libs: ["junit"],
+}
diff --git a/tests/Internal/ApplicationSharedMemoryTest32/AndroidManifest.xml b/tests/Internal/ApplicationSharedMemoryTest32/AndroidManifest.xml
new file mode 100644
index 0000000..4e1058e
--- /dev/null
+++ b/tests/Internal/ApplicationSharedMemoryTest32/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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="com.android.internal.tests">
+    <application
+        android:use32bitAbi="true"
+        android:multiArch="true">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.internal.tests"
+         android:label="Internal Tests"/>
+</manifest>
diff --git a/tests/Internal/ApplicationSharedMemoryTest32/AndroidTest.xml b/tests/Internal/ApplicationSharedMemoryTest32/AndroidTest.xml
new file mode 100644
index 0000000..9bde8b7
--- /dev/null
+++ b/tests/Internal/ApplicationSharedMemoryTest32/AndroidTest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+<configuration description="Runs tests for internal classes/utilities.">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="ApplicationSharedMemoryTest32.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="InternalTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.internal.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+            value="/data/user/0/com.android.internal.tests/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
\ No newline at end of file
diff --git a/tests/Internal/ApplicationSharedMemoryTest32/OWNERS b/tests/Internal/ApplicationSharedMemoryTest32/OWNERS
new file mode 100644
index 0000000..1ff3fac
--- /dev/null
+++ b/tests/Internal/ApplicationSharedMemoryTest32/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/PERFORMANCE_OWNERS
\ No newline at end of file
diff --git a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
new file mode 100644
index 0000000..e3a129f
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.os;
+
+import java.io.IOException;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.junit.Before;
+
+import java.io.FileDescriptor;
+
+/** Tests for {@link TimeoutRecord}. */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ApplicationSharedMemoryTest {
+
+    @Before
+    public void setUp() {
+        // Skip tests if the feature under test is disabled.
+        assumeTrue(Flags.applicationSharedMemoryEnabled());
+    }
+
+    /**
+     * Every application process, including ours, should have had an instance installed at this
+     * point.
+     */
+    @Test
+    public void hasInstance() {
+        // This shouldn't throw and shouldn't return null.
+        assertNotNull(ApplicationSharedMemory.getInstance());
+    }
+
+    /** Any app process should be able to read shared memory values. */
+    @Test
+    public void canRead() {
+        ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance();
+        instance.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
+        // Don't actually care about the value of the above.
+    }
+
+    /** Application processes should not have mutable access. */
+    @Test
+    public void appInstanceNotMutable() {
+        ApplicationSharedMemory instance = ApplicationSharedMemory.getInstance();
+        try {
+            instance.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(17);
+            fail("Attempted mutation in an app process should throw");
+        } catch (Exception expected) {
+        }
+    }
+
+    /** Instances share memory if they share the underlying memory region. */
+    @Test
+    public void instancesShareMemory() throws IOException {
+        ApplicationSharedMemory instance1 = ApplicationSharedMemory.create();
+        ApplicationSharedMemory instance2 =
+                ApplicationSharedMemory.fromFileDescriptor(
+                        instance1.getFileDescriptor(), /* mutable= */ true);
+        ApplicationSharedMemory instance3 =
+                ApplicationSharedMemory.fromFileDescriptor(
+                        instance2.getReadOnlyFileDescriptor(), /* mutable= */ false);
+
+        instance1.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(17);
+        assertEquals(
+                17, instance1.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                17, instance2.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                17, instance3.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+
+        instance2.setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(24);
+        assertEquals(
+                24, instance1.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                24, instance2.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+        assertEquals(
+                24, instance3.getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis());
+    }
+
+    /** Can't map read-only memory as mutable. */
+    @Test
+    public void readOnlyCantBeMutable() throws IOException {
+        ApplicationSharedMemory readWriteInstance = ApplicationSharedMemory.create();
+        FileDescriptor readOnlyFileDescriptor = readWriteInstance.getReadOnlyFileDescriptor();
+
+        try {
+            ApplicationSharedMemory.fromFileDescriptor(readOnlyFileDescriptor, /* mutable= */ true);
+            fail("Shouldn't be able to map read-only memory as mutable");
+        } catch (Exception expected) {
+        }
+    }
+}
diff --git a/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTestRule.java b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTestRule.java
new file mode 100644
index 0000000..ff2a461
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/os/ApplicationSharedMemoryTestRule.java
@@ -0,0 +1,56 @@
+/*
+ * 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.os;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import com.android.internal.os.ApplicationSharedMemory;
+
+/** Test rule that sets up and tears down ApplicationSharedMemory for test. */
+public class ApplicationSharedMemoryTestRule implements TestRule {
+
+    private ApplicationSharedMemory mSavedInstance;
+
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                setup();
+                try {
+                    base.evaluate(); // Run the test
+                } finally {
+                    teardown();
+                }
+            }
+        };
+    }
+
+    private void setup() {
+        mSavedInstance = ApplicationSharedMemory.sInstance;
+        ApplicationSharedMemory.sInstance = ApplicationSharedMemory.create();
+    }
+
+    private void teardown() {
+        ApplicationSharedMemory.sInstance.close();
+        ApplicationSharedMemory.sInstance = mSavedInstance;
+        mSavedInstance = null;
+    }
+}
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index c0e90f9..8d143b6 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -727,7 +727,17 @@
         when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
                 ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
         when(mSpyContext.getPackageManager()).thenReturn(mMockPackageManager);
-
+        try {
+            when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).then(inv -> {
+                final PackageInfo res = new PackageInfo();
+                res.packageName = inv.getArgument(0);
+                res.setApexPackageName(res.packageName);
+                res.setLongVersionCode(VERSION_CODE);
+                return res;
+            });
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
         watchdog.registerHealthObserver(rollbackObserver);
         return rollbackObserver;
     }
@@ -787,8 +797,10 @@
             // Verify controller by default is started when packages are ready
             assertThat(controller.mIsEnabled).isTrue();
 
-            verify(mConnectivityModuleConnector).registerHealthListener(
-                    mConnectivityModuleCallbackCaptor.capture());
+            if (!Flags.refactorCrashrecovery()) {
+                verify(mConnectivityModuleConnector).registerHealthListener(
+                        mConnectivityModuleCallbackCaptor.capture());
+            }
         }
         mAllocatedWatchdogs.add(watchdog);
         return watchdog;
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 5b17825..0364781a 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -46,6 +46,9 @@
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.test.TestLooper;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.util.AtomicFile;
@@ -111,6 +114,9 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
+    @Rule
+    public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private final TestClock mTestClock = new TestClock();
     private TestLooper mTestLooper;
     private Context mSpyContext;
@@ -966,6 +972,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_CRASHRECOVERY)
     public void testNetworkStackFailure() {
         mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
         final PackageWatchdog wd = createWatchdog();
@@ -986,6 +993,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_CRASHRECOVERY)
     public void testNetworkStackFailureRecoverabilityDetection() {
         final PackageWatchdog wd = createWatchdog();
 
@@ -1758,8 +1766,10 @@
             // Verify controller by default is started when packages are ready
             assertThat(controller.mIsEnabled).isTrue();
 
-            verify(mConnectivityModuleConnector).registerHealthListener(
-                    mConnectivityModuleCallbackCaptor.capture());
+            if (!Flags.refactorCrashrecovery()) {
+                verify(mConnectivityModuleConnector).registerHealthListener(
+                        mConnectivityModuleCallbackCaptor.capture());
+            }
         }
         mAllocatedWatchdogs.add(watchdog);
         return watchdog;
diff --git a/tests/Tracing/TEST_MAPPING b/tests/Tracing/TEST_MAPPING
index 7f58fce..f6e5221 100644
--- a/tests/Tracing/TEST_MAPPING
+++ b/tests/Tracing/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "TracingTests"
     }
diff --git a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index cfb2645..6f3deab 100644
--- a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.protolog;
 
-import static android.tools.traces.Utils.executeShellCommand;
+import static android.tools.traces.Utils.busyWaitForDataSourceRegistration;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
@@ -50,7 +50,6 @@
 import com.android.internal.protolog.common.LogLevel;
 
 import com.google.common.truth.Truth;
-import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.junit.After;
 import org.junit.Before;
@@ -60,14 +59,12 @@
 import org.junit.runners.JUnit4;
 import org.mockito.Mockito;
 
-import perfetto.protos.PerfettoConfig.TracingServiceState;
 import perfetto.protos.Protolog;
 import perfetto.protos.ProtologCommon;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
-import java.util.Optional;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -184,7 +181,7 @@
                     TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService);
         }
 
-        waitDataSourceIsAvailable();
+        busyWaitForDataSourceRegistration(TEST_PROTOLOG_DATASOURCE_NAME);
     }
 
     @Before
@@ -870,54 +867,6 @@
                 .isEqualTo("This message should also be logged 567");
     }
 
-    private static void waitDataSourceIsAvailable() {
-        final int timeoutMs = 10000;
-        final int busyWaitIntervalMs = 100;
-
-        int elapsedMs = 0;
-
-        while (!isDataSourceAvailable()) {
-            SystemClock.sleep(busyWaitIntervalMs);
-            elapsedMs += busyWaitIntervalMs;
-            if (elapsedMs >= timeoutMs) {
-                throw new RuntimeException("Data source didn't become available."
-                        + " Waited for: " + timeoutMs + " ms");
-            }
-        }
-    }
-
-    private static boolean isDataSourceAvailable() {
-        byte[] proto = executeShellCommand("perfetto --query-raw");
-
-        try {
-            TracingServiceState state = TracingServiceState.parseFrom(proto);
-
-            Optional<Integer> producerId = Optional.empty();
-
-            for (TracingServiceState.Producer producer : state.getProducersList()) {
-                if (producer.getPid() == android.os.Process.myPid()) {
-                    producerId = Optional.of(producer.getId());
-                    break;
-                }
-            }
-
-            if (producerId.isEmpty()) {
-                return false;
-            }
-
-            for (TracingServiceState.DataSource ds : state.getDataSourcesList()) {
-                if (ds.getDsDescriptor().getName().equals(TEST_PROTOLOG_DATASOURCE_NAME)
-                        && ds.getProducerId() == producerId.get()) {
-                    return true;
-                }
-            }
-        } catch (InvalidProtocolBufferException e) {
-            throw new RuntimeException(e);
-        }
-
-        return false;
-    }
-
     private enum TestProtoLogGroup implements IProtoLogGroup {
         TEST_GROUP(true, true, false, "TEST_TAG");
 
diff --git a/tests/broadcasts/OWNERS b/tests/broadcasts/OWNERS
new file mode 100644
index 0000000..d2e1f81
--- /dev/null
+++ b/tests/broadcasts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316181
+include platform/frameworks/base:/BROADCASTS_OWNERS
diff --git a/tests/broadcasts/unit/Android.bp b/tests/broadcasts/unit/Android.bp
new file mode 100644
index 0000000..47166a7
--- /dev/null
+++ b/tests/broadcasts/unit/Android.bp
@@ -0,0 +1,45 @@
+//
+// 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+    default_team: "trendy_team_framework_backstage_power",
+}
+
+android_test {
+    name: "BroadcastUnitTests",
+    srcs: ["src/**/*.java"],
+    defaults: [
+        "modules-utils-extended-mockito-rule-defaults",
+    ],
+    static_libs: [
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "mockito-target-extended-minus-junit4",
+        "truth",
+        "flag-junit",
+        "android.app.flags-aconfig-java",
+    ],
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
diff --git a/tests/broadcasts/unit/AndroidManifest.xml b/tests/broadcasts/unit/AndroidManifest.xml
new file mode 100644
index 0000000..e9c5248
--- /dev/null
+++ b/tests/broadcasts/unit/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.broadcasts.unit" >
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.broadcasts.unit"
+            android:label="Broadcasts Unit Tests"/>
+</manifest>
\ No newline at end of file
diff --git a/tests/broadcasts/unit/AndroidTest.xml b/tests/broadcasts/unit/AndroidTest.xml
new file mode 100644
index 0000000..b91e4783
--- /dev/null
+++ b/tests/broadcasts/unit/AndroidTest.xml
@@ -0,0 +1,29 @@
+<!-- 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.
+-->
+<configuration description="Runs Broadcasts tests">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-tag" value="BroadcastUnitTests" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="BroadcastUnitTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.broadcasts.unit" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/broadcasts/unit/TEST_MAPPING b/tests/broadcasts/unit/TEST_MAPPING
new file mode 100644
index 0000000..0e824c5
--- /dev/null
+++ b/tests/broadcasts/unit/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+    "postsubmit": [
+        {
+            "name": "BroadcastUnitTests",
+            "options": [
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                {
+                    "exclude-annotation": "org.junit.Ignore"
+                }
+            ]
+        }
+    ]
+}
diff --git a/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java
new file mode 100644
index 0000000..b7c412d
--- /dev/null
+++ b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.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 android.app;
+
+import static android.content.Intent.ACTION_BATTERY_CHANGED;
+import static android.content.Intent.ACTION_DEVICE_STORAGE_LOW;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.ArrayMap;
+
+import androidx.annotation.GuardedBy;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BroadcastStickyCacheTest {
+    @ClassRule
+    public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+            .mockStatic(SystemProperties.class)
+            .build();
+
+    private static final String PROP_KEY_BATTERY_CHANGED = BroadcastStickyCache.getKey(
+            ACTION_BATTERY_CHANGED);
+
+    private final TestSystemProps mTestSystemProps = new TestSystemProps();
+
+    @Before
+    public void setUp() {
+        doAnswer(invocation -> {
+            final String name = invocation.getArgument(0);
+            final long value = Long.parseLong(invocation.getArgument(1));
+            mTestSystemProps.add(name, value);
+            return null;
+        }).when(() -> SystemProperties.set(anyString(), anyString()));
+        doAnswer(invocation -> {
+            final String name = invocation.getArgument(0);
+            final TestSystemProps.Handle testHandle = mTestSystemProps.query(name);
+            if (testHandle == null) {
+                return null;
+            }
+            final SystemProperties.Handle handle = Mockito.mock(SystemProperties.Handle.class);
+            doAnswer(handleInvocation -> testHandle.getLong(-1)).when(handle).getLong(anyLong());
+            return handle;
+        }).when(() -> SystemProperties.find(anyString()));
+    }
+
+    @After
+    public void tearDown() {
+        mTestSystemProps.clear();
+        BroadcastStickyCache.clearForTest();
+    }
+
+    @Test
+    public void testUseCache_nullFilter() {
+        assertThat(BroadcastStickyCache.useCache(null)).isEqualTo(false);
+    }
+
+    @Test
+    public void testUseCache_noActions() {
+        final IntentFilter filter = new IntentFilter();
+        assertThat(BroadcastStickyCache.useCache(filter)).isEqualTo(false);
+    }
+
+    @Test
+    public void testUseCache_multipleActions() {
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_DEVICE_STORAGE_LOW);
+        filter.addAction(ACTION_BATTERY_CHANGED);
+        assertThat(BroadcastStickyCache.useCache(filter)).isEqualTo(false);
+    }
+
+    @Test
+    public void testUseCache_valueNotSet() {
+        final IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED);
+        assertThat(BroadcastStickyCache.useCache(filter)).isEqualTo(false);
+    }
+
+    @Test
+    public void testUseCache() {
+        final IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED);
+        final Intent intent = new Intent(ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_LEVEL, 90);
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+        BroadcastStickyCache.add(filter, intent);
+        assertThat(BroadcastStickyCache.useCache(filter)).isEqualTo(true);
+    }
+
+    @Test
+    public void testUseCache_versionMismatch() {
+        final IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED);
+        final Intent intent = new Intent(ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_LEVEL, 90);
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+        BroadcastStickyCache.add(filter, intent);
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+
+        assertThat(BroadcastStickyCache.useCache(filter)).isEqualTo(false);
+    }
+
+    @Test
+    public void testAdd() {
+        final IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED);
+        Intent intent = new Intent(ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_LEVEL, 90);
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+        BroadcastStickyCache.add(filter, intent);
+        assertThat(BroadcastStickyCache.useCache(filter)).isEqualTo(true);
+        Intent actualIntent = BroadcastStickyCache.getIntentUnchecked(filter);
+        assertThat(actualIntent).isNotNull();
+        assertEquals(actualIntent, intent);
+
+        intent = new Intent(ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_LEVEL, 99);
+        BroadcastStickyCache.add(filter, intent);
+        actualIntent = BroadcastStickyCache.getIntentUnchecked(filter);
+        assertThat(actualIntent).isNotNull();
+        assertEquals(actualIntent, intent);
+    }
+
+    @Test
+    public void testIncrementVersion_propExists() {
+        SystemProperties.set(PROP_KEY_BATTERY_CHANGED, String.valueOf(100));
+
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(101);
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(102);
+    }
+
+    @Test
+    public void testIncrementVersion_propNotExists() {
+        // Verify that the property doesn't exist
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(-1);
+
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(1);
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(2);
+    }
+
+    @Test
+    public void testIncrementVersionIfExists_propExists() {
+        BroadcastStickyCache.incrementVersion(ACTION_BATTERY_CHANGED);
+
+        BroadcastStickyCache.incrementVersionIfExists(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(2);
+        BroadcastStickyCache.incrementVersionIfExists(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(3);
+    }
+
+    @Test
+    public void testIncrementVersionIfExists_propNotExists() {
+        // Verify that the property doesn't exist
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(-1);
+
+        BroadcastStickyCache.incrementVersionIfExists(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(-1);
+        // Verify that property is not added as part of the querying.
+        BroadcastStickyCache.incrementVersionIfExists(ACTION_BATTERY_CHANGED);
+        assertThat(mTestSystemProps.get(PROP_KEY_BATTERY_CHANGED, -1 /* def */)).isEqualTo(-1);
+    }
+
+    private void assertEquals(Intent actualIntent, Intent expectedIntent) {
+        assertThat(actualIntent.getAction()).isEqualTo(expectedIntent.getAction());
+        assertEquals(actualIntent.getExtras(), expectedIntent.getExtras());
+    }
+
+    private void assertEquals(Bundle actualExtras, Bundle expectedExtras) {
+        assertWithMessage("Extras expected=%s, actual=%s", expectedExtras, actualExtras)
+                .that(actualExtras.kindofEquals(expectedExtras)).isTrue();
+    }
+
+    private static final class TestSystemProps {
+        @GuardedBy("mSysProps")
+        private final ArrayMap<String, Long> mSysProps = new ArrayMap<>();
+
+        public void add(String name, long value) {
+            synchronized (mSysProps) {
+                mSysProps.put(name, value);
+            }
+        }
+
+        public long get(String name, long defaultValue) {
+            synchronized (mSysProps) {
+                final int idx = mSysProps.indexOfKey(name);
+                return idx >= 0 ? mSysProps.valueAt(idx) : defaultValue;
+            }
+        }
+
+        public Handle query(String name) {
+            synchronized (mSysProps) {
+                return mSysProps.containsKey(name) ? new Handle(name) : null;
+            }
+        }
+
+        public void clear() {
+            synchronized (mSysProps) {
+                mSysProps.clear();
+            }
+        }
+
+        public class Handle {
+            private final String mName;
+
+            Handle(String name) {
+                mName = name;
+            }
+
+            public long getLong(long defaultValue) {
+                return get(mName, defaultValue);
+            }
+        }
+    }
+}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index df1d51e..064b461 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -346,6 +346,21 @@
             value->value->Accept(&body_printer);
             printer->Undent();
           }
+          printer->Println("Flag disabled values:");
+          for (const auto& value : entry.flag_disabled_values) {
+            printer->Print("(");
+            printer->Print(value->config.to_string());
+            printer->Print(") ");
+            value->value->Accept(&headline_printer);
+            if (options.show_sources && !value->value->GetSource().path.empty()) {
+              printer->Print(" src=");
+              printer->Print(value->value->GetSource().to_string());
+            }
+            printer->Println();
+            printer->Indent();
+            value->value->Accept(&body_printer);
+            printer->Undent();
+          }
           printer->Undent();
         }
       }
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index a274f04..0d261ab 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -71,6 +71,17 @@
 
 enum class FlagStatus { NoFlag = 0, Disabled = 1, Enabled = 2 };
 
+struct FeatureFlagAttribute {
+  std::string name;
+  bool negated = false;
+
+  std::string ToString() {
+    return (negated ? "!" : "") + name;
+  }
+
+  bool operator==(const FeatureFlagAttribute& o) const = default;
+};
+
 android::StringPiece to_string(ResourceType type);
 
 /**
@@ -232,6 +243,12 @@
 
   // Exported symbols
   std::vector<SourcedResourceName> exported_symbols;
+
+  // Flag status
+  FlagStatus flag_status = FlagStatus::NoFlag;
+
+  // Flag
+  std::optional<FeatureFlagAttribute> flag;
 };
 
 /**
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index a5aecc8..fce6aa7 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -107,9 +107,10 @@
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool staged_api = false;
   bool allow_new = false;
-  FlagStatus flag_status = FlagStatus::NoFlag;
   std::optional<OverlayableItem> overlayable_item;
   std::optional<StagedId> staged_alias;
+  std::optional<FeatureFlagAttribute> flag;
+  FlagStatus flag_status;
 
   std::string comment;
   std::unique_ptr<Value> value;
@@ -151,6 +152,7 @@
   }
 
   if (res->value != nullptr) {
+    res->value->SetFlag(res->flag);
     res->value->SetFlagStatus(res->flag_status);
     // Attach the comment, source and config to the value.
     res->value->SetComment(std::move(res->comment));
@@ -162,8 +164,6 @@
     res_builder.SetStagedId(res->staged_alias.value());
   }
 
-  res_builder.SetFlagStatus(res->flag_status);
-
   bool error = false;
   if (!res->name.entry.empty()) {
     if (!table->AddResource(res_builder.Build(), diag)) {
@@ -546,12 +546,26 @@
       {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
   });
 
-  std::string resource_type = parser->element_name();
-  auto flag_status = GetFlagStatus(parser);
-  if (!flag_status) {
-    return false;
+  std::string_view resource_type = parser->element_name();
+  if (auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"))) {
+    if (options_.flag) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Resource flag are not allowed both in the path and in the file");
+      return false;
+    }
+    out_resource->flag = std::move(flag);
+    std::string error;
+    auto flag_status = GetFlagStatus(out_resource->flag, options_.feature_flag_values, &error);
+    if (flag_status) {
+      out_resource->flag_status = flag_status.value();
+    } else {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << error);
+      return false;
+    }
+  } else if (options_.flag) {
+    out_resource->flag = options_.flag;
+    out_resource->flag_status = options_.flag_status;
   }
-  out_resource->flag_status = flag_status.value();
 
   // The value format accepted for this resource.
   uint32_t resource_format = 0u;
@@ -567,7 +581,7 @@
 
     // Items have their type encoded in the type attribute.
     if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
-      resource_type = std::string(maybe_type.value());
+      resource_type = maybe_type.value();
     } else {
       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
                    << "<item> must have a 'type' attribute");
@@ -590,7 +604,7 @@
 
     // Bags have their type encoded in the type attribute.
     if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
-      resource_type = std::string(maybe_type.value());
+      resource_type = maybe_type.value();
     } else {
       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
                    << "<bag> must have a 'type' attribute");
@@ -733,33 +747,6 @@
   return false;
 }
 
-std::optional<FlagStatus> ResourceParser::GetFlagStatus(xml::XmlPullParser* parser) {
-  auto flag_status = FlagStatus::NoFlag;
-
-  std::optional<StringPiece> flag = xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag");
-  if (flag) {
-    auto flag_it = options_.feature_flag_values.find(flag.value());
-    if (flag_it == options_.feature_flag_values.end()) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Resource flag value undefined");
-      return {};
-    }
-    const auto& flag_properties = flag_it->second;
-    if (!flag_properties.read_only) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Only read only flags may be used with resources");
-      return {};
-    }
-    if (!flag_properties.enabled.has_value()) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Only flags with a value may be used with resources");
-      return {};
-    }
-    flag_status = flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled;
-  }
-  return flag_status;
-}
-
 bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
                                ParsedResource* out_resource,
                                const uint32_t format) {
@@ -1666,21 +1653,25 @@
     const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
     if (element_namespace.empty() && element_name == "item") {
-      auto flag_status = GetFlagStatus(parser);
-      if (!flag_status) {
-        error = true;
-        continue;
-      }
+      auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"));
       std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
       if (!item) {
         diag_->Error(android::DiagMessage(item_source) << "could not parse array item");
         error = true;
         continue;
       }
-      item->SetFlagStatus(flag_status.value());
+      item->SetFlag(flag);
+      std::string err;
+      auto status = GetFlagStatus(flag, options_.feature_flag_values, &err);
+      if (status) {
+        item->SetFlagStatus(status.value());
+      } else {
+        diag_->Error(android::DiagMessage(item_source) << err);
+        error = true;
+        continue;
+      }
       item->SetSource(item_source);
       array->elements.emplace_back(std::move(item));
-
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
                    << "unknown tag <" << element_namespace << ":" << element_name << ">");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 442dea8..90690d5 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -57,6 +57,11 @@
   std::optional<Visibility::Level> visibility;
 
   FeatureFlagValues feature_flag_values;
+
+  // The flag that should be applied to all resources parsed
+  std::optional<FeatureFlagAttribute> flag;
+
+  FlagStatus flag_status = FlagStatus::NoFlag;
 };
 
 struct FlattenedXmlSubTree {
@@ -85,8 +90,6 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceParser);
 
-  std::optional<FlagStatus> GetFlagStatus(xml::XmlPullParser* parser);
-
   std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
 
   // Parses the XML subtree as a StyleString (flattened XML representation for strings with
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 9751459..5435cba2 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -101,6 +101,21 @@
   }
 };
 
+struct ConfigFlagKey {
+  const ConfigDescription* config;
+  StringPiece product;
+  const FeatureFlagAttribute& flag;
+};
+
+struct lt_config_flag_key_ref {
+  template <typename T>
+  bool operator()(const T& lhs, const ConfigFlagKey& rhs) const noexcept {
+    return std::tie(lhs->config, lhs->product, lhs->value->GetFlag()->name,
+                    lhs->value->GetFlag()->negated) <
+           std::tie(*rhs.config, rhs.product, rhs.flag.name, rhs.flag.negated);
+  }
+};
+
 }  // namespace
 
 ResourceTable::ResourceTable(ResourceTable::Validation validation) : validation_(validation) {
@@ -213,6 +228,25 @@
   return results;
 }
 
+ResourceConfigValue* ResourceEntry::FindOrCreateFlagDisabledValue(
+    const FeatureFlagAttribute& flag, const android::ConfigDescription& config,
+    android::StringPiece product) {
+  auto iter = std::lower_bound(flag_disabled_values.begin(), flag_disabled_values.end(),
+                               ConfigFlagKey{&config, product, flag}, lt_config_flag_key_ref());
+  if (iter != flag_disabled_values.end()) {
+    ResourceConfigValue* value = iter->get();
+    const auto value_flag = value->value->GetFlag().value();
+    if (value_flag.name == flag.name && value_flag.negated == flag.negated &&
+        value->config == config && value->product == product) {
+      return value;
+    }
+  }
+  ResourceConfigValue* newValue =
+      flag_disabled_values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))
+          ->get();
+  return newValue;
+}
+
 bool ResourceEntry::HasDefaultValue() const {
   // The default config should be at the top of the list, since the list is sorted.
   return !values.empty() && values.front()->config == ConfigDescription::DefaultConfig();
@@ -375,13 +409,14 @@
   }
 };
 
-void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePackage* package,
-                              const ResourceTableType* type, const std::string& entry_name,
-                              const std::optional<ResourceId>& id, const Visibility& visibility,
-                              const std::optional<AllowNew>& allow_new,
-                              const std::optional<OverlayableItem>& overlayable_item,
-                              const std::optional<StagedId>& staged_id,
-                              const std::vector<std::unique_ptr<ResourceConfigValue>>& values) {
+void InsertEntryIntoTableView(
+    ResourceTableView& table, const ResourceTablePackage* package, const ResourceTableType* type,
+    const std::string& entry_name, const std::optional<ResourceId>& id,
+    const Visibility& visibility, const std::optional<AllowNew>& allow_new,
+    const std::optional<OverlayableItem>& overlayable_item,
+    const std::optional<StagedId>& staged_id,
+    const std::vector<std::unique_ptr<ResourceConfigValue>>& values,
+    const std::vector<std::unique_ptr<ResourceConfigValue>>& flag_disabled_values) {
   SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
   SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
   SortedVectorInserter<ResourceTableEntryView, EntryViewComparer> entry_inserter;
@@ -408,6 +443,9 @@
   for (auto& value : values) {
     new_entry.values.emplace_back(value.get());
   }
+  for (auto& value : flag_disabled_values) {
+    new_entry.flag_disabled_values.emplace_back(value.get());
+  }
 
   entry_inserter.Insert(view_type->entries, std::move(new_entry));
 }
@@ -426,6 +464,21 @@
   return nullptr;
 }
 
+const ResourceConfigValue* ResourceTableEntryView::FindFlagDisabledValue(
+    const FeatureFlagAttribute& flag, const ConfigDescription& config,
+    android::StringPiece product) const {
+  auto iter = std::lower_bound(flag_disabled_values.begin(), flag_disabled_values.end(),
+                               ConfigFlagKey{&config, product, flag}, lt_config_flag_key_ref());
+  if (iter != values.end()) {
+    const ResourceConfigValue* value = *iter;
+    if (value->value->GetFlag() == flag && value->config == config &&
+        StringPiece(value->product) == product) {
+      return value;
+    }
+  }
+  return nullptr;
+}
+
 ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptions& options) const {
   ResourceTableView view;
   for (const auto& package : packages) {
@@ -433,13 +486,13 @@
       for (const auto& entry : type->entries) {
         InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, entry->id,
                                  entry->visibility, entry->allow_new, entry->overlayable_item,
-                                 entry->staged_id, entry->values);
+                                 entry->staged_id, entry->values, entry->flag_disabled_values);
 
         if (options.create_alias_entries && entry->staged_id) {
           auto alias_id = entry->staged_id.value().id;
           InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, alias_id,
                                    entry->visibility, entry->allow_new, entry->overlayable_item, {},
-                                   entry->values);
+                                   entry->values, entry->flag_disabled_values);
         }
       }
     }
@@ -587,6 +640,25 @@
     entry->staged_id = res.staged_id.value();
   }
 
+  if (res.value != nullptr && res.value->GetFlagStatus() == FlagStatus::Disabled) {
+    auto disabled_config_value =
+        entry->FindOrCreateFlagDisabledValue(res.value->GetFlag().value(), res.config, res.product);
+    if (!disabled_config_value->value) {
+      // Resource does not exist, add it now.
+      // Must clone the value since it might be in the values vector as well
+      CloningValueTransformer cloner(&string_pool);
+      disabled_config_value->value = res.value->Transform(cloner);
+    } else {
+      diag->Error(android::DiagMessage(source)
+                  << "duplicate value for resource '" << res.name << "' " << "with config '"
+                  << res.config << "' and flag '"
+                  << (res.value->GetFlag().value().negated ? "!" : "")
+                  << res.value->GetFlag().value().name << "'");
+      diag->Error(android::DiagMessage(source) << "resource previously defined here");
+      return false;
+    }
+  }
+
   if (res.value != nullptr) {
     auto config_value = entry->FindOrCreateValue(res.config, res.product);
     if (!config_value->value) {
@@ -595,9 +667,9 @@
     } else {
       // When validation is enabled, ensure that a resource cannot have multiple values defined for
       // the same configuration unless protected by flags.
-      auto result =
-          validate ? ResolveFlagCollision(config_value->value->GetFlagStatus(), res.flag_status)
-                   : CollisionResult::kKeepBoth;
+      auto result = validate ? ResolveFlagCollision(config_value->value->GetFlagStatus(),
+                                                    res.value->GetFlagStatus())
+                             : CollisionResult::kKeepBoth;
       if (result == CollisionResult::kConflict) {
         result = ResolveValueCollision(config_value->value.get(), res.value.get());
       }
@@ -771,11 +843,6 @@
   return *this;
 }
 
-NewResourceBuilder& NewResourceBuilder::SetFlagStatus(FlagStatus flag_status) {
-  res_.flag_status = flag_status;
-  return *this;
-}
-
 NewResource NewResourceBuilder::Build() {
   return std::move(res_);
 }
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index cba6b70..b0e1855 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -136,6 +136,9 @@
   // The resource's values for each configuration.
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
 
+  // The resource's values that are behind disabled flags.
+  std::vector<std::unique_ptr<ResourceConfigValue>> flag_disabled_values;
+
   explicit ResourceEntry(android::StringPiece name) : name(name) {
   }
 
@@ -148,6 +151,13 @@
                                          android::StringPiece product);
   std::vector<ResourceConfigValue*> FindAllValues(const android::ConfigDescription& config);
 
+  // Either returns the existing ResourceConfigValue in the disabled list with the given flag,
+  // config, and product or creates a new one and returns that. In either case the returned value
+  // does not have the flag set on the value so it must be set by the caller.
+  ResourceConfigValue* FindOrCreateFlagDisabledValue(const FeatureFlagAttribute& flag,
+                                                     const android::ConfigDescription& config,
+                                                     android::StringPiece product = {});
+
   template <typename Func>
   std::vector<ResourceConfigValue*> FindValuesIf(Func f) {
     std::vector<ResourceConfigValue*> results;
@@ -215,9 +225,14 @@
   std::optional<OverlayableItem> overlayable_item;
   std::optional<StagedId> staged_id;
   std::vector<const ResourceConfigValue*> values;
+  std::vector<const ResourceConfigValue*> flag_disabled_values;
 
   const ResourceConfigValue* FindValue(const android::ConfigDescription& config,
                                        android::StringPiece product = {}) const;
+
+  const ResourceConfigValue* FindFlagDisabledValue(const FeatureFlagAttribute& flag,
+                                                   const android::ConfigDescription& config,
+                                                   android::StringPiece product = {}) const;
 };
 
 struct ResourceTableTypeView {
@@ -269,7 +284,6 @@
   std::optional<AllowNew> allow_new;
   std::optional<StagedId> staged_id;
   bool allow_mangled = false;
-  FlagStatus flag_status = FlagStatus::NoFlag;
 };
 
 struct NewResourceBuilder {
@@ -283,7 +297,6 @@
   NewResourceBuilder& SetAllowNew(AllowNew allow_new);
   NewResourceBuilder& SetStagedId(StagedId id);
   NewResourceBuilder& SetAllowMangled(bool allow_mangled);
-  NewResourceBuilder& SetFlagStatus(FlagStatus flag_status);
   NewResource Build();
 
  private:
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b75e87c..723cfc0 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -1102,6 +1102,7 @@
 std::unique_ptr<T> CopyValueFields(std::unique_ptr<T> new_value, const T* value) {
   new_value->SetSource(value->GetSource());
   new_value->SetComment(value->GetComment());
+  new_value->SetFlag(value->GetFlag());
   new_value->SetFlagStatus(value->GetFlagStatus());
   return new_value;
 }
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index a1b1839..e000c65 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -65,10 +65,21 @@
     return translatable_;
   }
 
+  void SetFlag(std::optional<FeatureFlagAttribute> val) {
+    flag_ = val;
+  }
+
+  std::optional<FeatureFlagAttribute> GetFlag() const {
+    return flag_;
+  }
+
   void SetFlagStatus(FlagStatus val) {
     flag_status_ = val;
   }
 
+  // If the value is behind a flag this returns whether that flag was enabled when the value was
+  // parsed by comparing it to the flags passed on the command line to aapt2 (taking into account
+  // negation if necessary). If there was no flag, FlagStatus::NoFlag is returned instead.
   FlagStatus GetFlagStatus() const {
     return flag_status_;
   }
@@ -128,6 +139,7 @@
   std::string comment_;
   bool weak_ = false;
   bool translatable_ = true;
+  std::optional<FeatureFlagAttribute> flag_;
   FlagStatus flag_status_ = FlagStatus::NoFlag;
 
  private:
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 5c64089..a0f60b6 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -240,6 +240,9 @@
 
   // The staged resource ID of this finalized resource.
   StagedId staged_id = 7;
+
+  // The set of values defined for this entry which are behind disabled flags
+  repeated ConfigValue flag_disabled_config_value = 8;
 }
 
 // A Configuration/Value pair.
@@ -283,6 +286,8 @@
 
   // The status of the flag the value is behind if any
   uint32 flag_status = 8;
+  bool flag_negated = 9;
+  string flag_name = 10;
 }
 
 // A CompoundValue is an abstract type. It represents a value that is a made of other values.
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
index b0ed3da3..f4735a2 100644
--- a/tools/aapt2/ResourcesInternal.proto
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -49,4 +49,9 @@
 
   // Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file).
   repeated Symbol exported_symbol = 5;
+
+  // The status of the flag the file is behind if any
+  uint32 flag_status = 6;
+  bool flag_negated = 7;
+  string flag_name = 8;
 }
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 2a978a5..52372fa 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -67,6 +67,7 @@
   std::string resource_dir;
   std::string name;
   std::string extension;
+  std::string flag_name;
 
   // Original config str. We keep this because when we parse the config, we may add on
   // version qualifiers. We want to preserve the original input so the output is easily
@@ -81,6 +82,22 @@
                                                                std::string* out_error,
                                                                const CompileOptions& options) {
   std::vector<std::string> parts = util::Split(path, dir_sep);
+
+  std::string flag_name;
+  // Check for a flag
+  for (auto iter = parts.begin(); iter != parts.end();) {
+    if (iter->starts_with("flag(") && iter->ends_with(")")) {
+      if (!flag_name.empty()) {
+        if (out_error) *out_error = "resource path cannot contain more than one flag directory";
+        return {};
+      }
+      flag_name = iter->substr(5, iter->size() - 6);
+      iter = parts.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+
   if (parts.size() < 2) {
     if (out_error) *out_error = "bad resource path";
     return {};
@@ -131,6 +148,7 @@
                           std::string(dir_str),
                           std::string(name),
                           std::string(extension),
+                          std::move(flag_name),
                           std::string(config_str),
                           config};
 }
@@ -142,6 +160,9 @@
     name << "-" << data.config_str;
   }
   name << "_" << data.name;
+  if (!data.flag_name.empty()) {
+    name << ".(" << data.flag_name << ")";
+  }
   if (!data.extension.empty()) {
     name << "." << data.extension;
   }
@@ -163,7 +184,6 @@
                                        << "failed to open file: " << fin->GetError());
       return false;
     }
-
     // Parse the values file from XML.
     xml::XmlPullParser xml_parser(fin.get());
 
@@ -176,6 +196,18 @@
     // If visibility was forced, we need to use it when creating a new resource and also error if
     // we try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
     parser_options.visibility = options.visibility;
+    parser_options.flag = ParseFlag(path_data.flag_name);
+
+    if (parser_options.flag) {
+      std::string error;
+      auto flag_status = GetFlagStatus(parser_options.flag, options.feature_flag_values, &error);
+      if (flag_status) {
+        parser_options.flag_status = std::move(flag_status.value());
+      } else {
+        context->GetDiagnostics()->Error(android::DiagMessage(path_data.source) << error);
+        return false;
+      }
+    }
 
     ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
         parser_options);
@@ -402,6 +434,18 @@
   xmlres->file.config = path_data.config;
   xmlres->file.source = path_data.source;
   xmlres->file.type = ResourceFile::Type::kProtoXml;
+  xmlres->file.flag = ParseFlag(path_data.flag_name);
+
+  if (xmlres->file.flag) {
+    std::string error;
+    auto flag_status = GetFlagStatus(xmlres->file.flag, options.feature_flag_values, &error);
+    if (flag_status) {
+      xmlres->file.flag_status = flag_status.value();
+    } else {
+      context->GetDiagnostics()->Error(android::DiagMessage(path_data.source) << error);
+      return false;
+    }
+  }
 
   // Collect IDs that are defined here.
   XmlIdCollector collector;
@@ -491,6 +535,27 @@
   res_file.source = path_data.source;
   res_file.type = ResourceFile::Type::kPng;
 
+  if (!path_data.flag_name.empty()) {
+    FeatureFlagAttribute flag;
+    auto name = path_data.flag_name;
+    if (name.starts_with('!')) {
+      flag.negated = true;
+      flag.name = name.substr(1);
+    } else {
+      flag.name = name;
+    }
+    res_file.flag = flag;
+
+    std::string error;
+    auto flag_status = GetFlagStatus(flag, options.feature_flag_values, &error);
+    if (flag_status) {
+      res_file.flag_status = flag_status.value();
+    } else {
+      context->GetDiagnostics()->Error(android::DiagMessage(path_data.source) << error);
+      return false;
+    }
+  }
+
   {
     auto data = file->OpenAsData();
     if (!data) {
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 6da3176..d3750a6 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -138,6 +138,22 @@
     }
   }
 
+  for (const ResourceConfigValue* config_value_a : entry_a.flag_disabled_values) {
+    auto config_value_b = entry_b.FindFlagDisabledValue(config_value_a->value->GetFlag().value(),
+                                                        config_value_a->config);
+    if (!config_value_b) {
+      std::stringstream str_stream;
+      str_stream << "missing disabled value " << pkg_a.name << ":" << type_a.named_type << "/"
+                 << entry_a.name << " config=" << config_value_a->config
+                 << " flag=" << config_value_a->value->GetFlag()->ToString();
+      EmitDiffLine(apk_b->GetSource(), str_stream.str());
+      diff = true;
+    } else {
+      diff |= EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a,
+                                          apk_b, pkg_b, type_b, entry_b, config_value_b);
+    }
+  }
+
   // Check for any newly added config values.
   for (const ResourceConfigValue* config_value_b : entry_b.values) {
     auto config_value_a = entry_a.FindValue(config_value_b->config);
@@ -149,6 +165,18 @@
       diff = true;
     }
   }
+  for (const ResourceConfigValue* config_value_b : entry_b.flag_disabled_values) {
+    auto config_value_a = entry_a.FindFlagDisabledValue(config_value_b->value->GetFlag().value(),
+                                                        config_value_b->config);
+    if (!config_value_a) {
+      std::stringstream str_stream;
+      str_stream << "new disabled config " << pkg_b.name << ":" << type_b.named_type << "/"
+                 << entry_b.name << " config=" << config_value_b->config
+                 << " flag=" << config_value_b->value->GetFlag()->ToString();
+      EmitDiffLine(apk_b->GetSource(), str_stream.str());
+      diff = true;
+    }
+  }
   return diff;
 }
 
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 7739171..08f8f0d 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -34,6 +34,44 @@
 
 namespace aapt {
 
+std::optional<FeatureFlagAttribute> ParseFlag(std::optional<std::string_view> flag_text) {
+  if (!flag_text || flag_text->empty()) {
+    return {};
+  }
+  FeatureFlagAttribute flag;
+  if (flag_text->starts_with('!')) {
+    flag.negated = true;
+    flag.name = flag_text->substr(1);
+  } else {
+    flag.name = flag_text.value();
+  }
+  return flag;
+}
+
+std::optional<FlagStatus> GetFlagStatus(const std::optional<FeatureFlagAttribute>& flag,
+                                        const FeatureFlagValues& feature_flag_values,
+                                        std::string* out_err) {
+  if (!flag) {
+    return FlagStatus::NoFlag;
+  }
+  auto flag_it = feature_flag_values.find(flag->name);
+  if (flag_it == feature_flag_values.end()) {
+    *out_err = "Resource flag value undefined: " + flag->name;
+    return {};
+  }
+  const auto& flag_properties = flag_it->second;
+  if (!flag_properties.read_only) {
+    *out_err = "Only read only flags may be used with resources: " + flag->name;
+    return {};
+  }
+  if (!flag_properties.enabled.has_value()) {
+    *out_err = "Only flags with a value may be used with resources: " + flag->name;
+    return {};
+  }
+  return (flag_properties.enabled.value() != flag->negated) ? FlagStatus::Enabled
+                                                            : FlagStatus::Disabled;
+}
+
 std::optional<uint16_t> ParseTargetDensityParameter(StringPiece arg, android::IDiagnostics* diag) {
   ConfigDescription preferred_density_config;
   if (!ConfigDescription::Parse(arg, &preferred_density_config)) {
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 6b8813b..d32e532 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -49,6 +49,12 @@
 
 using FeatureFlagValues = std::map<std::string, FeatureFlagProperties, std::less<>>;
 
+std::optional<FeatureFlagAttribute> ParseFlag(std::optional<std::string_view> flag_text);
+
+std::optional<FlagStatus> GetFlagStatus(const std::optional<FeatureFlagAttribute>& flag,
+                                        const FeatureFlagValues& feature_flag_values,
+                                        std::string* out_err);
+
 // Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
 // Returns Nothing and logs a human friendly error message if the string was not legal.
 std::optional<uint16_t> ParseTargetDensityParameter(android::StringPiece arg,
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 55f5e56..8583cad 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -536,6 +536,34 @@
 
         config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
                                                      &out_table->string_pool, files, out_error);
+
+        if (config_value->value == nullptr) {
+          return false;
+        }
+      }
+
+      // flag disabled
+      for (const pb::ConfigValue& pb_config_value : pb_entry.flag_disabled_config_value()) {
+        const pb::Configuration& pb_config = pb_config_value.config();
+
+        ConfigDescription config;
+        if (!DeserializeConfigFromPb(pb_config, &config, out_error)) {
+          return false;
+        }
+
+        FeatureFlagAttribute flag;
+        flag.name = pb_config_value.value().item().flag_name();
+        flag.negated = pb_config_value.value().item().flag_negated();
+        ResourceConfigValue* config_value =
+            entry->FindOrCreateFlagDisabledValue(std::move(flag), config, pb_config.product());
+        if (config_value->value != nullptr) {
+          *out_error = "duplicate configuration in resource table";
+          return false;
+        }
+
+        config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
+                                                     &out_table->string_pool, files, out_error);
+
         if (config_value->value == nullptr) {
           return false;
         }
@@ -615,6 +643,12 @@
   out_file->source.path = pb_file.source_path();
   out_file->type = DeserializeFileReferenceTypeFromPb(pb_file.type());
 
+  out_file->flag_status = (FlagStatus)pb_file.flag_status();
+  if (!pb_file.flag_name().empty()) {
+    out_file->flag =
+        FeatureFlagAttribute{.name = pb_file.flag_name(), .negated = pb_file.flag_negated()};
+  }
+
   std::string config_error;
   if (!DeserializeConfigFromPb(pb_file.config(), &out_file->config, &config_error)) {
     std::ostringstream error;
@@ -748,7 +782,6 @@
     if (value == nullptr) {
       return {};
     }
-
   } else if (pb_value.has_compound_value()) {
     const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
     switch (pb_compound_value.value_case()) {
@@ -1018,6 +1051,12 @@
       DeserializeItemFromPbInternal(pb_item, src_pool, config, value_pool, files, out_error);
   if (item) {
     item->SetFlagStatus((FlagStatus)pb_item.flag_status());
+    if (!pb_item.flag_name().empty()) {
+      FeatureFlagAttribute flag;
+      flag.name = pb_item.flag_name();
+      flag.negated = pb_item.flag_negated();
+      item->SetFlag(std::move(flag));
+    }
   }
   return item;
 }
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 5772b3b..d83fe91 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -427,6 +427,14 @@
           SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
                              source_pool.get());
         }
+
+        for (const ResourceConfigValue* config_value : entry.flag_disabled_values) {
+          pb::ConfigValue* pb_config_value = pb_entry->add_flag_disabled_config_value();
+          SerializeConfig(config_value->config, pb_config_value->mutable_config());
+          pb_config_value->mutable_config()->set_product(config_value->product);
+          SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
+                             source_pool.get());
+        }
       }
     }
   }
@@ -721,6 +729,11 @@
   }
   if (out_value->has_item()) {
     out_value->mutable_item()->set_flag_status((uint32_t)value.GetFlagStatus());
+    if (value.GetFlag()) {
+      const auto& flag = value.GetFlag();
+      out_value->mutable_item()->set_flag_negated(flag->negated);
+      out_value->mutable_item()->set_flag_name(flag->name);
+    }
   }
 }
 
@@ -730,6 +743,11 @@
   item.Accept(&serializer);
   out_item->MergeFrom(value.item());
   out_item->set_flag_status((uint32_t)item.GetFlagStatus());
+  if (item.GetFlag()) {
+    const auto& flag = item.GetFlag();
+    out_item->set_flag_negated(flag->negated);
+    out_item->set_flag_name(flag->name);
+  }
 }
 
 void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
@@ -737,6 +755,11 @@
   out_file->set_source_path(file.source.path);
   out_file->set_type(SerializeFileReferenceTypeToPb(file.type));
   SerializeConfig(file.config, out_file->mutable_config());
+  out_file->set_flag_status((uint32_t)file.flag_status);
+  if (file.flag) {
+    out_file->set_flag_negated(file.flag->negated);
+    out_file->set_flag_name(file.flag->name);
+  }
 
   for (const SourcedResourceName& exported : file.exported_symbols) {
     pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol();
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
index c456e5c..7160b35 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
@@ -31,13 +31,29 @@
         "res/values/ints.xml",
         "res/values/strings.xml",
         "res/layout/layout1.xml",
+        "res/layout/layout3.xml",
+        "res/flag(test.package.falseFlag)/values/bools.xml",
+        "res/flag(test.package.falseFlag)/layout/layout2.xml",
+        "res/flag(test.package.falseFlag)/drawable/removedpng.png",
+        "res/flag(test.package.trueFlag)/layout/layout3.xml",
+        "res/values/flag(test.package.trueFlag)/bools.xml",
+        "res/values/flag(!test.package.trueFlag)/bools.xml",
+        "res/values/flag(!test.package.falseFlag)/bools.xml",
     ],
     out: [
+        "drawable_removedpng.(test.package.falseFlag).png.flat",
         "values_bools.arsc.flat",
+        "values_bools.(test.package.falseFlag).arsc.flat",
+        "values_bools.(test.package.trueFlag).arsc.flat",
+        "values_bools.(!test.package.falseFlag).arsc.flat",
+        "values_bools.(!test.package.trueFlag).arsc.flat",
         "values_bools2.arsc.flat",
         "values_ints.arsc.flat",
         "values_strings.arsc.flat",
         "layout_layout1.xml.flat",
+        "layout_layout2.(test.package.falseFlag).xml.flat",
+        "layout_layout3.xml.flat",
+        "layout_layout3.(test.package.trueFlag).xml.flat",
     ],
     cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
         "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/drawable/removedpng.png" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/drawable/removedpng.png"
new file mode 100644
index 0000000..8a9e698
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/drawable/removedpng.png"
Binary files differ
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/layout/layout2.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/layout/layout2.xml"
new file mode 100644
index 0000000..dec5de7
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/layout/layout2.xml"
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+</LinearLayout>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/values/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/values/bools.xml"
new file mode 100644
index 0000000..c46c4d4
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/values/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool7">false</bool>
+</resources>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.trueFlag\051/layout/layout3.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.trueFlag\051/layout/layout3.xml"
new file mode 100644
index 0000000..5aeee0e
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.trueFlag\051/layout/layout3.xml"
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+    <TextView android:id="@+id/text1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="foobar" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/layout/layout3.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/layout/layout3.xml
new file mode 100644
index 0000000..dec5de7
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/layout/layout3.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
index 1ed0c8a..35975ed 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
@@ -9,4 +9,15 @@
     <bool name="bool3">false</bool>
 
     <bool name="bool4" android:featureFlag="test.package.falseFlag">true</bool>
+
+    <bool name="bool5">false</bool>
+    <bool name="bool5" android:featureFlag="!test.package.falseFlag">true</bool>
+
+    <bool name="bool6">true</bool>
+    <bool name="bool6" android:featureFlag="!test.package.trueFlag">false</bool>
+
+    <bool name="bool7">true</bool>
+    <bool name="bool8">false</bool>
+    <bool name="bool9">true</bool>
+    <bool name="bool10">false</bool>
 </resources>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.falseFlag\051/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.falseFlag\051/bools.xml"
new file mode 100644
index 0000000..a63749c
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.falseFlag\051/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool10">true</bool>
+</resources>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.trueFlag\051/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.trueFlag\051/bools.xml"
new file mode 100644
index 0000000..bb5526e
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.trueFlag\051/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool9">false</bool>
+</resources>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050test.package.trueFlag\051/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050test.package.trueFlag\051/bools.xml"
new file mode 100644
index 0000000..eba780e
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050test.package.trueFlag\051/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool8">true</bool>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
index 3db37c2..6293008 100644
--- a/tools/aapt2/link/FlaggedResources_test.cpp
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -17,6 +17,7 @@
 #include "LoadedApk.h"
 #include "cmd/Dump.h"
 #include "io/StringStream.h"
+#include "test/Common.h"
 #include "test/Test.h"
 #include "text/Printer.h"
 
@@ -75,6 +76,10 @@
 
   std::string output;
   DumpResourceTableToString(loaded_apk.get(), &output);
+  ASSERT_EQ(output.find("bool4"), std::string::npos);
+  ASSERT_EQ(output.find("str1"), std::string::npos);
+  ASSERT_EQ(output.find("layout2"), std::string::npos);
+  ASSERT_EQ(output.find("removedpng"), std::string::npos);
 }
 
 TEST_F(FlaggedResourcesTest, DisabledResourcesRemovedFromTableChunks) {
@@ -86,6 +91,8 @@
 
   ASSERT_EQ(output.find("bool4"), std::string::npos);
   ASSERT_EQ(output.find("str1"), std::string::npos);
+  ASSERT_EQ(output.find("layout2"), std::string::npos);
+  ASSERT_EQ(output.find("removedpng"), std::string::npos);
 }
 
 TEST_F(FlaggedResourcesTest, DisabledResourcesInRJava) {
@@ -98,4 +105,47 @@
   ASSERT_NE(r_contents.find("public static final int str1"), std::string::npos);
 }
 
+TEST_F(FlaggedResourcesTest, TwoValuesSameDisabledFlag) {
+  test::TestDiagnosticsImpl diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_FALSE(CompileFile(
+      GetTestPath("res/values/values.xml"),
+      R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
+           <bool name="bool1" android:featureFlag="test.package.falseFlag">false</bool>
+           <bool name="bool1" android:featureFlag="test.package.falseFlag">true</bool>
+         </resources>)",
+      compiled_files_dir, &diag,
+      {"--feature-flags", "test.package.falseFlag:ro=false,test.package.trueFlag:ro=true"}));
+  ASSERT_TRUE(diag.GetLog().contains("duplicate value for resource 'bool/bool1'"));
+}
+
+TEST_F(FlaggedResourcesTest, TwoValuesSameDisabledFlagDifferentFiles) {
+  test::TestDiagnosticsImpl diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(
+      GetTestPath("res/values/values1.xml"),
+      R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
+           <bool name="bool1" android:featureFlag="test.package.falseFlag">false</bool>
+         </resources>)",
+      compiled_files_dir, &diag,
+      {"--feature-flags", "test.package.falseFlag:ro=false,test.package.trueFlag:ro=true"}));
+  ASSERT_TRUE(CompileFile(
+      GetTestPath("res/values/values2.xml"),
+      R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
+           <bool name="bool1" android:featureFlag="test.package.falseFlag">true</bool>
+         </resources>)",
+      compiled_files_dir, &diag,
+      {"--feature-flags", "test.package.falseFlag:ro=false,test.package.trueFlag:ro=true"}));
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest",
+      GetDefaultManifest(),
+      "-o",
+      out_apk,
+  };
+
+  ASSERT_FALSE(Link(link_args, compiled_files_dir, &diag));
+  ASSERT_TRUE(diag.GetLog().contains("duplicate value for resource 'bool1'"));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 37a039e..1bef5f8 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -321,6 +321,30 @@
           }
         }
       }
+
+      // disabled values
+      for (auto& src_config_value : src_entry->flag_disabled_values) {
+        auto dst_config_value = dst_entry->FindOrCreateFlagDisabledValue(
+            src_config_value->value->GetFlag().value(), src_config_value->config,
+            src_config_value->product);
+        if (!dst_config_value->value) {
+          // Resource does not exist, add it now.
+          // Must clone the value since it might be in the values vector as well
+          CloningValueTransformer cloner(&main_table_->string_pool);
+          dst_config_value->value = src_config_value->value->Transform(cloner);
+        } else {
+          error = true;
+          context_->GetDiagnostics()->Error(
+              android::DiagMessage(src_config_value->value->GetSource())
+              << "duplicate value for resource '" << src_entry->name << "' " << "with config '"
+              << src_config_value->config << "' and flag '"
+              << (src_config_value->value->GetFlag()->negated ? "!" : "")
+              << src_config_value->value->GetFlag()->name << "'");
+          context_->GetDiagnostics()->Note(
+              android::DiagMessage(dst_config_value->value->GetSource())
+              << "resource previously defined here");
+        }
+      }
     }
   }
   return !error;
@@ -353,6 +377,8 @@
   file_ref->SetSource(file_desc.source);
   file_ref->type = file_desc.type;
   file_ref->file = file;
+  file_ref->SetFlagStatus(file_desc.flag_status);
+  file_ref->SetFlag(file_desc.flag);
 
   ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
   pkg->FindOrCreateType(file_desc.name.type)
diff --git a/tools/aapt2/test/Common.cpp b/tools/aapt2/test/Common.cpp
index cdf24534..c7dd4c9 100644
--- a/tools/aapt2/test/Common.cpp
+++ b/tools/aapt2/test/Common.cpp
@@ -21,23 +21,6 @@
 namespace aapt {
 namespace test {
 
-struct TestDiagnosticsImpl : public android::IDiagnostics {
-  void Log(Level level, android::DiagMessageActual& actual_msg) override {
-    switch (level) {
-      case Level::Note:
-        return;
-
-      case Level::Warn:
-        std::cerr << actual_msg.source << ": warn: " << actual_msg.message << "." << std::endl;
-        break;
-
-      case Level::Error:
-        std::cerr << actual_msg.source << ": error: " << actual_msg.message << "." << std::endl;
-        break;
-    }
-  }
-};
-
 android::IDiagnostics* GetDiagnostics() {
   static TestDiagnosticsImpl diag;
   return &diag;
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 0437980..b06c432 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -37,6 +37,32 @@
 namespace aapt {
 namespace test {
 
+struct TestDiagnosticsImpl : public android::IDiagnostics {
+  void Log(Level level, android::DiagMessageActual& actual_msg) override {
+    switch (level) {
+      case Level::Note:
+        return;
+
+      case Level::Warn:
+        std::cerr << actual_msg.source << ": warn: " << actual_msg.message << "." << std::endl;
+        log << actual_msg.source << ": warn: " << actual_msg.message << "." << std::endl;
+        break;
+
+      case Level::Error:
+        std::cerr << actual_msg.source << ": error: " << actual_msg.message << "." << std::endl;
+        log << actual_msg.source << ": error: " << actual_msg.message << "." << std::endl;
+        break;
+    }
+  }
+
+  std::string GetLog() {
+    return log.str();
+  }
+
+ private:
+  std::ostringstream log;
+};
+
 android::IDiagnostics* GetDiagnostics();
 
 inline ResourceName ParseNameOrDie(android::StringPiece str) {
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index b91abe5..570bcf1 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -91,10 +91,13 @@
 }
 
 bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
-                                     android::StringPiece out_dir, android::IDiagnostics* diag) {
+                                     android::StringPiece out_dir, android::IDiagnostics* diag,
+                                     const std::vector<android::StringPiece>& additional_args) {
   WriteFile(path, contents);
   CHECK(file::mkdirs(out_dir.data()));
-  return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0;
+  std::vector<android::StringPiece> args = {path, "-o", out_dir, "-v"};
+  args.insert(args.end(), additional_args.begin(), additional_args.end());
+  return CompileCommand(diag).Execute(args, &std::cerr) == 0;
 }
 
 bool CommandTestFixture::Link(const std::vector<std::string>& args, android::IDiagnostics* diag) {
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index 14298d16..178d011 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -73,7 +73,8 @@
   // Wries the contents of the file to the specified path. The file is compiled and the flattened
   // file is written to the out directory.
   bool CompileFile(const std::string& path, const std::string& contents,
-                   android::StringPiece flat_out_dir, android::IDiagnostics* diag);
+                   android::StringPiece flat_out_dir, android::IDiagnostics* diag,
+                   const std::vector<android::StringPiece>& additional_args = {});
 
   // Executes the link command with the specified arguments.
   bool Link(const std::vector<std::string>& args, android::IDiagnostics* diag);
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
new file mode 100644
index 0000000..501fd65
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
@@ -0,0 +1,34 @@
+/*
+ * 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.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * @hide
+ */
+@Target({METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestIgnore {
+}
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
index eba8e62..001943c 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -24,6 +24,9 @@
 --remove-annotation
     android.hosttest.annotation.HostSideTestRemove
 
+--ignore-annotation
+    android.hosttest.annotation.HostSideTestIgnore
+
 --substitute-annotation
     android.hosttest.annotation.HostSideTestSubstitute
 
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 34aaaa9..165bb57 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -166,6 +166,7 @@
             options.keepClassAnnotations,
             options.throwAnnotations,
             options.removeAnnotations,
+            options.ignoreAnnotations,
             options.substituteAnnotations,
             options.redirectAnnotations,
             options.redirectionClassAnnotations,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 057a52c..b083d89 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -84,6 +84,7 @@
         var keepAnnotations: MutableSet<String> = mutableSetOf(),
         var throwAnnotations: MutableSet<String> = mutableSetOf(),
         var removeAnnotations: MutableSet<String> = mutableSetOf(),
+        var ignoreAnnotations: MutableSet<String> = mutableSetOf(),
         var keepClassAnnotations: MutableSet<String> = mutableSetOf(),
         var redirectAnnotations: MutableSet<String> = mutableSetOf(),
 
@@ -184,6 +185,9 @@
                         "--remove-annotation" ->
                             ret.removeAnnotations.addUniqueAnnotationArg()
 
+                        "--ignore-annotation" ->
+                            ret.ignoreAnnotations.addUniqueAnnotationArg()
+
                         "--substitute-annotation" ->
                             ret.substituteAnnotations.addUniqueAnnotationArg()
 
@@ -277,6 +281,7 @@
               keepAnnotations=$keepAnnotations,
               throwAnnotations=$throwAnnotations,
               removeAnnotations=$removeAnnotations,
+              ignoreAnnotations=$ignoreAnnotations,
               keepClassAnnotations=$keepClassAnnotations,
               substituteAnnotations=$substituteAnnotations,
               nativeSubstituteAnnotations=$redirectionClassAnnotations,
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index a6b8cdb..36adf06 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -48,6 +48,7 @@
     keepClassAnnotations_: Set<String>,
     throwAnnotations_: Set<String>,
     removeAnnotations_: Set<String>,
+    ignoreAnnotations_: Set<String>,
     substituteAnnotations_: Set<String>,
     redirectAnnotations_: Set<String>,
     redirectionClassAnnotations_: Set<String>,
@@ -60,6 +61,7 @@
     private val keepClassAnnotations = convertToInternalNames(keepClassAnnotations_)
     private val throwAnnotations = convertToInternalNames(throwAnnotations_)
     private val removeAnnotations = convertToInternalNames(removeAnnotations_)
+    private val ignoreAnnotations = convertToInternalNames(ignoreAnnotations_)
     private val redirectAnnotations = convertToInternalNames(redirectAnnotations_)
     private val substituteAnnotations = convertToInternalNames(substituteAnnotations_)
     private val redirectionClassAnnotations =
@@ -73,6 +75,7 @@
             keepClassAnnotations +
             throwAnnotations +
             removeAnnotations +
+            ignoreAnnotations +
             redirectAnnotations +
             substituteAnnotations
 
@@ -107,6 +110,7 @@
             in substituteAnnotations -> FilterPolicy.Substitute.withReason(REASON_ANNOTATION)
             in throwAnnotations -> FilterPolicy.Throw.withReason(REASON_ANNOTATION)
             in removeAnnotations -> FilterPolicy.Remove.withReason(REASON_ANNOTATION)
+            in ignoreAnnotations -> FilterPolicy.Ignore.withReason(REASON_ANNOTATION)
             in redirectAnnotations -> FilterPolicy.Redirect.withReason(REASON_ANNOTATION)
             else -> null
         }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
index 7440b94..d6aa761 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
@@ -27,16 +27,22 @@
 class ClassFilter private constructor(
     private val defaultResult: Boolean,
 ) {
+    private enum class MatchType {
+        Full,
+        Prefix,
+        Suffix,
+    }
+
     private class FilterElement(
         val allowed: Boolean,
         val internalName: String,
-        val isPrefix: Boolean,
+        val matchType: MatchType,
     ) {
         fun matches(classInternalName: String): Boolean {
-            return if (isPrefix) {
-                classInternalName.startsWith(internalName)
-            } else {
-                classInternalName == internalName
+            return when (matchType) {
+                MatchType.Full -> classInternalName == internalName
+                MatchType.Prefix -> classInternalName.startsWith(internalName)
+                MatchType.Suffix -> classInternalName.endsWith(internalName)
             }
         }
     }
@@ -114,15 +120,29 @@
 
                 // Special case -- matches any class names.
                 if (line == "*") {
-                    ret.elements.add(FilterElement(allow, "", true))
+                    ret.elements.add(FilterElement(allow, "", MatchType.Prefix))
                     return@forEach
                 }
 
-                // Handle wildcard -- e.g. "package.name.*"
+                // Handle prefix match -- e.g. "package.name.*"
                 if (line.endsWith(".*")) {
                     ret.elements.add(
                         FilterElement(
-                            allow, line.substring(0, line.length - 2).toJvmClassName(), true
+                            allow,
+                            line.substring(0, line.length - 2).toJvmClassName() + "/",
+                            MatchType.Prefix
+                        )
+                    )
+                    return@forEach
+                }
+
+                // Handle suffix match -- e.g. "*.Flags"
+                if (line.startsWith("*.")) {
+                    ret.elements.add(
+                        FilterElement(
+                            allow,
+                            "/" + line.substring(2, line.length).toJvmClassName(),
+                            MatchType.Suffix
                         )
                     )
                     return@forEach
@@ -136,10 +156,10 @@
                         lineNo
                     )
                 }
-                ret.elements.add(FilterElement(allow, line.toJvmClassName(), false))
+                ret.elements.add(FilterElement(allow, line.toJvmClassName(), MatchType.Suffix))
             }
 
             return ret
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 82586bb..103e152 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -21,6 +21,26 @@
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
+## Class: android/hosttest/annotation/HostSideTestIgnore.class
+  Compiled from "HostSideTestIgnore.java"
+public interface android.hosttest.annotation.HostSideTestIgnore extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestIgnore
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestIgnore.java"
+RuntimeVisibleAnnotations:
+  x: #x(#x=[e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
 ## Class: android/hosttest/annotation/HostSideTestKeep.class
   Compiled from "HostSideTestKeep.java"
 public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
@@ -382,7 +402,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 2
+  interfaces: 0, fields: 2, methods: 9, attributes: 2
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -522,6 +542,24 @@
     RuntimeInvisibleAnnotations:
       x: #x()
         android.hosttest.annotation.HostSideTestThrow
+
+  public int toBeIgnored();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String not supported on host side
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestIgnore
 }
 SourceFile: "TinyFrameworkAnnotations.java"
 RuntimeInvisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
index 31bbcc5..eeec554 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -432,7 +432,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 6, attributes: 3
+  interfaces: 0, fields: 1, methods: 7, attributes: 3
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -554,6 +554,22 @@
     RuntimeInvisibleAnnotations:
       x: #x()
         android.hosttest.annotation.HostSideTestThrow
+
+  public int toBeIgnored();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_0
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestIgnore
 }
 SourceFile: "TinyFrameworkAnnotations.java"
 RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index 41f459a..0f8af92 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -593,7 +593,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 6, attributes: 3
+  interfaces: 0, fields: 1, methods: 7, attributes: 3
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -743,6 +743,27 @@
     RuntimeInvisibleAnnotations:
       x: #x()
         android.hosttest.annotation.HostSideTestThrow
+
+  public int toBeIgnored();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                 // String toBeIgnored
+         x: ldc           #x                 // String ()I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_0
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestIgnore
 }
 SourceFile: "TinyFrameworkAnnotations.java"
 RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
index ed0fa26..3415deb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
@@ -16,6 +16,7 @@
 package com.android.hoststubgen.test.tinyframework;
 
 import android.hosttest.annotation.HostSideTestClassLoadHook;
+import android.hosttest.annotation.HostSideTestIgnore;
 import android.hosttest.annotation.HostSideTestKeep;
 import android.hosttest.annotation.HostSideTestRemove;
 import android.hosttest.annotation.HostSideTestSubstitute;
@@ -71,4 +72,9 @@
     public String unsupportedMethod() {
         return "This value shouldn't be seen on the host side.";
     }
+
+    @HostSideTestIgnore
+    public int toBeIgnored() {
+        throw new RuntimeException("not supported on host side");
+    }
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
index 34c98e9..1816b38 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
@@ -102,4 +102,11 @@
         assertThat(new TinyFrameworkNestedClasses.StaticNestedClass.Double$NestedClass().value)
                 .isEqualTo(8);
     }
+
+    @Test
+    public void testIgnoreAnnotation() {
+        // The actual method will throw, but because of @Ignore, it'll return 0.
+        assertThat(new TinyFrameworkAnnotations().toBeIgnored())
+                .isEqualTo(0);
+    }
 }
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
index 85b6e80..d4e75d4 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
@@ -69,6 +69,8 @@
         assertThat(f.matches("d/e/f")).isEqualTo(false)
         assertThat(f.matches("d/e/f/g")).isEqualTo(true)
         assertThat(f.matches("x")).isEqualTo(true)
+
+        assertThat(f.matches("ab/x")).isEqualTo(true)
     }
 
     @Test
@@ -96,4 +98,18 @@
             assertThat(e.message).contains("line 1")
         }
     }
+
+    @Test
+    fun testSuffix() {
+        val f = ClassFilter.buildFromString("""
+            *.Abc       # allow
+            !*          # Disallow by default
+            """.trimIndent(), true, "X")
+        assertThat(f.matches("a/b/c")).isEqualTo(false)
+        assertThat(f.matches("a/Abc")).isEqualTo(true)
+        assertThat(f.matches("a/b/c/Abc")).isEqualTo(true)
+        assertThat(f.matches("a/b/c/Abc\$Nested")).isEqualTo(true)
+
+        assertThat(f.matches("a/XyzAbc")).isEqualTo(false)
+    }
 }
\ No newline at end of file
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt
index 675a59e..caa018d 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/ExemptAidlInterfaces.kt
@@ -256,7 +256,7 @@
     "android.devicelock.IGetDeviceIdCallback",
     "android.devicelock.IGetKioskAppsCallback",
     "android.devicelock.IIsDeviceLockedCallback",
-    "android.devicelock.IVoidResultCallback",
+    "android.devicelock.ILockUnlockDeviceCallback",
     "android.federatedcompute.aidl.IExampleStoreCallback",
     "android.federatedcompute.aidl.IExampleStoreIterator",
     "android.federatedcompute.aidl.IExampleStoreIteratorCallback",
@@ -364,8 +364,6 @@
     "android.health.connect.aidl.IGetPriorityResponseCallback",
     "android.health.connect.aidl.IHealthConnectService",
     "android.health.connect.aidl.IInsertRecordsResponseCallback",
-    "android.health.connect.aidl.IMedicalDataSourceResponseCallback",
-    "android.health.connect.aidl.IMedicalResourcesResponseCallback",
     "android.health.connect.aidl.IMigrationCallback",
     "android.health.connect.aidl.IReadMedicalResourcesResponseCallback",
     "android.health.connect.aidl.IReadRecordsResponseCallback",
@@ -462,6 +460,7 @@
     "android.net.ipmemorystore.IOnBlobRetrievedListener",
     "android.net.ipmemorystore.IOnL2KeyResponseListener",
     "android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener",
+    "android.net.ipmemorystore.IOnNetworkEventCountRetrievedListener",
     "android.net.ipmemorystore.IOnSameL3NetworkResponseListener",
     "android.net.ipmemorystore.IOnStatusAndCountListener",
     "android.net.ipmemorystore.IOnStatusListener",
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/PermissionAnnotationDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/PermissionAnnotationDetector.kt
index d44c271..8d6e320 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/PermissionAnnotationDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/PermissionAnnotationDetector.kt
@@ -57,7 +57,10 @@
             ISSUE_MISSING_PERMISSION_ANNOTATION,
             node,
             context.getLocation(node),
-            "The method ${node.name} is not permission-annotated."
+            """
+                ${node.name} should be annotated with either @EnforcePermission, \
+                @RequiresNoPermission or @PermissionManuallyEnforced.
+            """.trimMargin()
         )
     }
 
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt
index 824be93..f985d02 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/PermissionAnnotationDetectorTest.kt
@@ -73,7 +73,7 @@
             .run()
             .expect(
                 """
-                src/frameworks/base/services/java/com/android/server/Bar.java:3: Error: The method testMethod is not permission-annotated. [MissingPermissionAnnotation]
+                src/frameworks/base/services/java/com/android/server/Bar.java:3: Error: testMethod should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced. [MissingPermissionAnnotation]
                     public void testMethod(int parameter1, int parameter2) { }
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                 1 errors, 0 warnings
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index a9e6328..590f719 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -30,8 +30,8 @@
     name: "systemfeatures-gen-tests-srcs",
     cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " +
         "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true --feature-apis=WATCH > $(location RoNoFeatures.java) && " +
-        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeatures --readonly=false --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: > $(location RwFeatures.java) && " +
-        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: --feature-apis=WATCH,PC > $(location RoFeatures.java)",
+        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeatures --readonly=false --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:UNAVAILABLE --feature=AUTO: > $(location RwFeatures.java) && " +
+        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:UNAVAILABLE --feature=AUTO: --feature-apis=WATCH,PC > $(location RoFeatures.java)",
     out: [
         "RwNoFeatures.java",
         "RoNoFeatures.java",
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index 5df453d..cba521e 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -20,7 +20,10 @@
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeSpec
+import java.util.HashMap
+import java.util.Map
 import javax.lang.model.element.Modifier
 
 /*
@@ -31,7 +34,7 @@
  *
  * <pre>
  *   <cmd> com.foo.RoSystemFeatures --readonly=true \
- *           --feature=WATCH:0 --feature=AUTOMOTIVE: --feature=VULKAN:9348
+ *           --feature=WATCH:0 --feature=AUTOMOTIVE: --feature=VULKAN:9348 --feature=PC:UNAVAILABLE
  *           --feature-apis=WATCH,PC,LEANBACK
  * </pre>
  *
@@ -43,12 +46,13 @@
  *     @AssumeTrueForR8
  *     public static boolean hasFeatureWatch(Context context);
  *     @AssumeFalseForR8
- *     public static boolean hasFeatureAutomotive(Context context);
+ *     public static boolean hasFeaturePc(Context context);
  *     @AssumeTrueForR8
  *     public static boolean hasFeatureVulkan(Context context);
- *     public static boolean hasFeaturePc(Context context);
+ *     public static boolean hasFeatureAutomotive(Context context);
  *     public static boolean hasFeatureLeanback(Context context);
  *     public static Boolean maybeHasFeature(String feature, int version);
+ *     public static ArrayMap<String, FeatureInfo> getCompileTimeAvailableFeatures();
  * }
  * </pre>
  */
@@ -58,6 +62,7 @@
     private const val READONLY_ARG = "--readonly="
     private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
     private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
+    private val FEATUREINFO_CLASS = ClassName.get("android.content.pm", "FeatureInfo")
     private val ASSUME_TRUE_CLASS =
         ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8")
     private val ASSUME_FALSE_CLASS =
@@ -67,7 +72,10 @@
         println("Usage: SystemFeaturesGenerator <outputClassName> [options]")
         println(" Options:")
         println("  --readonly=true|false    Whether to encode features as build-time constants")
-        println("  --feature=\$NAME:\$VER   A feature+version pair (blank version == disabled)")
+        println("  --feature=\$NAME:\$VER   A feature+version pair, where \$VER can be:")
+        println("                             * blank/empty == undefined (variable API)")
+        println("                             * valid int   == enabled   (constant API)")
+        println("                             * UNAVAILABLE == disabled  (constant API)")
         println("                           This will always generate associated query APIs,")
         println("                           adding to or replacing those from `--feature-apis=`.")
         println("  --feature-apis=\$NAME_1,\$NAME_2")
@@ -89,7 +97,7 @@
 
         var readonly = false
         var outputClassName: ClassName? = null
-        val featureArgs = mutableListOf<FeatureArg>()
+        val featureArgs = mutableListOf<FeatureInfo>()
         // We could just as easily hardcode this list, as the static API surface should change
         // somewhat infrequently, but this decouples the codegen from the framework completely.
         val featureApiArgs = mutableSetOf<String>()
@@ -122,7 +130,7 @@
         featureArgs.associateByTo(
             features,
             { it.name },
-            { FeatureInfo(it.name, it.version, readonly) },
+            { FeatureInfo(it.name, it.version, it.readonly && readonly) },
         )
 
         outputClassName
@@ -139,6 +147,7 @@
 
         addFeatureMethodsToClass(classBuilder, features.values)
         addMaybeFeatureMethodToClass(classBuilder, features.values)
+        addGetFeaturesMethodToClass(classBuilder, features.values)
 
         // TODO(b/203143243): Add validation of build vs runtime values to ensure consistency.
         JavaFile.builder(outputClassName.packageName(), classBuilder.build())
@@ -154,13 +163,17 @@
      * Parses a feature argument of the form "--feature=$NAME:$VER", where "$VER" is optional.
      *   * "--feature=WATCH:0" -> Feature enabled w/ version 0 (default version when enabled)
      *   * "--feature=WATCH:7" -> Feature enabled w/ version 7
-     *   * "--feature=WATCH:"  -> Feature disabled
+     *   * "--feature=WATCH:"  -> Feature status undefined, runtime API generated
+     *   * "--feature=WATCH:UNAVAILABLE"  -> Feature disabled
      */
-    private fun parseFeatureArg(arg: String): FeatureArg {
+    private fun parseFeatureArg(arg: String): FeatureInfo {
         val featureArgs = arg.substring(FEATURE_ARG.length).split(":")
         val name = parseFeatureName(featureArgs[0])
-        val version = featureArgs.getOrNull(1)?.toIntOrNull()
-        return FeatureArg(name, version)
+        return when (featureArgs.getOrNull(1)) {
+            null, "" -> FeatureInfo(name, null, readonly = false)
+            "UNAVAILABLE" -> FeatureInfo(name, null, readonly = true)
+            else -> FeatureInfo(name, featureArgs[1].toIntOrNull(), readonly = true)
+        }
     }
 
     private fun parseFeatureName(name: String): String =
@@ -218,7 +231,7 @@
     /*
      * Adds a generic query method to the class with the form: {@code public static boolean
      * maybeHasFeature(String featureName, int version)}, returning null if the feature version is
-     * undefined or not readonly.
+     * undefined or not (compile-time) readonly.
      *
      * This method is useful for internal usage within the framework, e.g., from the implementation
      * of {@link android.content.pm.PackageManager#hasSystemFeature(Context)}, when we may only
@@ -267,7 +280,41 @@
         builder.addMethod(methodBuilder.build())
     }
 
-    private data class FeatureArg(val name: String, val version: Int?)
+    /*
+     * Adds a method to get all compile-time enabled features.
+     *
+     * This method is useful for internal usage within the framework to augment
+     * any system features that are parsed from the various partitions.
+     */
+    private fun addGetFeaturesMethodToClass(
+        builder: TypeSpec.Builder,
+        features: Collection<FeatureInfo>,
+    ) {
+        val methodBuilder =
+                MethodSpec.methodBuilder("getCompileTimeAvailableFeatures")
+                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+                .addAnnotation(ClassName.get("android.annotation", "NonNull"))
+                .addJavadoc("Gets features marked as available at compile-time, keyed by name." +
+                        "\n\n@hide")
+                .returns(ParameterizedTypeName.get(
+                        ClassName.get(Map::class.java),
+                        ClassName.get(String::class.java),
+                        FEATUREINFO_CLASS))
+
+        val availableFeatures = features.filter { it.readonly && it.version != null }
+        methodBuilder.addStatement("Map<String, FeatureInfo> features = new \$T<>(\$L)",
+                HashMap::class.java, availableFeatures.size)
+        if (!availableFeatures.isEmpty()) {
+            methodBuilder.addStatement("FeatureInfo fi = new FeatureInfo()")
+        }
+        for (feature in availableFeatures) {
+            methodBuilder.addStatement("fi.name = \$T.\$N", PACKAGEMANAGER_CLASS, feature.name)
+            methodBuilder.addStatement("fi.version = \$L", feature.version)
+            methodBuilder.addStatement("features.put(fi.name, new FeatureInfo(fi))")
+        }
+        methodBuilder.addStatement("return features")
+        builder.addMethod(methodBuilder.build())
+    }
 
     private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean)
 }
diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
index 724639b..edbfc42 100644
--- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
@@ -3,16 +3,20 @@
 //            --readonly=true \
 //            --feature=WATCH:1 \
 //            --feature=WIFI:0 \
-//            --feature=VULKAN:-1 \
+//            --feature=VULKAN:UNAVAILABLE \
 //            --feature=AUTO: \
 //            --feature-apis=WATCH,PC
 package com.android.systemfeatures;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 import com.android.aconfig.annotations.AssumeFalseForR8;
 import com.android.aconfig.annotations.AssumeTrueForR8;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @hide
@@ -62,9 +66,8 @@
      *
      * @hide
      */
-    @AssumeFalseForR8
     public static boolean hasFeatureAuto(Context context) {
-        return false;
+        return hasFeatureFallback(context, PackageManager.FEATURE_AUTO);
     }
 
     private static boolean hasFeatureFallback(Context context, String featureName) {
@@ -79,10 +82,27 @@
         switch (featureName) {
             case PackageManager.FEATURE_WATCH: return 1 >= version;
             case PackageManager.FEATURE_WIFI: return 0 >= version;
-            case PackageManager.FEATURE_VULKAN: return -1 >= version;
-            case PackageManager.FEATURE_AUTO: return false;
+            case PackageManager.FEATURE_VULKAN: return false;
             default: break;
         }
         return null;
     }
+
+    /**
+     * Gets features marked as available at compile-time, keyed by name.
+     *
+     * @hide
+     */
+    @NonNull
+    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
+        Map<String, FeatureInfo> features = new HashMap<>(2);
+        FeatureInfo fi = new FeatureInfo();
+        fi.name = PackageManager.FEATURE_WATCH;
+        fi.version = 1;
+        features.put(fi.name, new FeatureInfo(fi));
+        fi.name = PackageManager.FEATURE_WIFI;
+        fi.version = 0;
+        features.put(fi.name, new FeatureInfo(fi));
+        return features;
+    }
 }
diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
index 59c5b4e..bf7a006 100644
--- a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
@@ -4,9 +4,13 @@
 //            --feature-apis=WATCH
 package com.android.systemfeatures;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @hide
@@ -32,4 +36,15 @@
     public static Boolean maybeHasFeature(String featureName, int version) {
         return null;
     }
+
+    /**
+     * Gets features marked as available at compile-time, keyed by name.
+     *
+     * @hide
+     */
+    @NonNull
+    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
+        Map<String, FeatureInfo> features = new HashMap<>(0);
+        return features;
+    }
 }
diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
index 6f89759..b20b228 100644
--- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
@@ -3,13 +3,17 @@
 //            --readonly=false \
 //            --feature=WATCH:1 \
 //            --feature=WIFI:0 \
-//            --feature=VULKAN:-1 \
+//            --feature=VULKAN:UNAVAILABLE \
 //            --feature=AUTO:
 package com.android.systemfeatures;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @hide
@@ -62,4 +66,15 @@
     public static Boolean maybeHasFeature(String featureName, int version) {
         return null;
     }
+
+    /**
+     * Gets features marked as available at compile-time, keyed by name.
+     *
+     * @hide
+     */
+    @NonNull
+    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
+        Map<String, FeatureInfo> features = new HashMap<>(0);
+        return features;
+    }
 }
diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
index 2111d56..d91f5b6 100644
--- a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
@@ -3,8 +3,12 @@
 //            --readonly=false
 package com.android.systemfeatures;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.FeatureInfo;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @hide
@@ -21,4 +25,15 @@
     public static Boolean maybeHasFeature(String featureName, int version) {
         return null;
     }
+
+    /**
+     * Gets features marked as available at compile-time, keyed by name.
+     *
+     * @hide
+     */
+    @NonNull
+    public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
+        Map<String, FeatureInfo> features = new HashMap<>(0);
+        return features;
+    }
 }
diff --git a/tools/systemfeatures/tests/src/FeatureInfo.java b/tools/systemfeatures/tests/src/FeatureInfo.java
new file mode 100644
index 0000000..9d57edc
--- /dev/null
+++ b/tools/systemfeatures/tests/src/FeatureInfo.java
@@ -0,0 +1,30 @@
+/*
+ * 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.content.pm;
+
+/** Stub for testing */
+public final class FeatureInfo {
+    public String name;
+    public int version;
+
+    public FeatureInfo() {}
+
+    public FeatureInfo(FeatureInfo orig) {
+        name = orig.name;
+        version = orig.version;
+    }
+}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
index 6dfd244..39f8fc4 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 
 import org.junit.Before;
@@ -36,6 +37,8 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.Map;
+
 @RunWith(JUnit4.class)
 public class SystemFeaturesGeneratorTest {
 
@@ -57,6 +60,7 @@
         assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+        assertThat(RwNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
     }
 
     @Test
@@ -68,6 +72,7 @@
         assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+        assertThat(RoNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
 
         // Also ensure we fall back to the PackageManager for feature APIs without an accompanying
         // versioned feature definition.
@@ -101,6 +106,7 @@
         assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
         assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
         assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+        assertThat(RwFeatures.getCompileTimeAvailableFeatures()).isEmpty();
     }
 
     @Test
@@ -110,10 +116,13 @@
         assertThat(RoFeatures.hasFeatureWatch(mContext)).isTrue();
         assertThat(RoFeatures.hasFeatureWifi(mContext)).isTrue();
         assertThat(RoFeatures.hasFeatureVulkan(mContext)).isFalse();
-        assertThat(RoFeatures.hasFeatureAuto(mContext)).isFalse();
         verify(mPackageManager, never()).hasSystemFeature(anyString(), anyInt());
 
-        // For defined feature types, conditional queries should reflect the build-time versions.
+        // For defined feature types, conditional queries should reflect either:
+        //  * Enabled if the feature version is specified
+        //  * Disabled if UNAVAILABLE is specified
+        //  * Unknown if no version value is provided
+
         // VERSION=1
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, -1)).isTrue();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isTrue();
@@ -124,15 +133,19 @@
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isTrue();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 100)).isFalse();
 
-        // VERSION=-1
-        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, -1)).isTrue();
+        // VERSION=UNAVAILABLE
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, -1)).isFalse();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isFalse();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 100)).isFalse();
 
-        // DISABLED
-        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, -1)).isFalse();
-        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isFalse();
-        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 100)).isFalse();
+        // VERSION=
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO, 0)).thenReturn(false);
+        assertThat(RoFeatures.hasFeatureAuto(mContext)).isFalse();
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO, 0)).thenReturn(true);
+        assertThat(RoFeatures.hasFeatureAuto(mContext)).isTrue();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, -1)).isNull();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 100)).isNull();
 
         // For feature APIs without an associated feature definition, conditional queries should
         // report null, and explicit queries should report runtime-defined versions.
@@ -148,5 +161,12 @@
         assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", -1)).isNull();
         assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
         assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull();
+        assertThat(RoFeatures.maybeHasFeature("", 0)).isNull();
+
+        Map<String, FeatureInfo> compiledFeatures = RoFeatures.getCompileTimeAvailableFeatures();
+        assertThat(compiledFeatures.keySet())
+                .containsExactly(PackageManager.FEATURE_WATCH, PackageManager.FEATURE_WIFI);
+        assertThat(compiledFeatures.get(PackageManager.FEATURE_WATCH).version).isEqualTo(1);
+        assertThat(compiledFeatures.get(PackageManager.FEATURE_WIFI).version).isEqualTo(0);
     }
 }
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index fc4a909..f68ae2c 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -173,10 +173,6 @@
                 }
             }
         }
-
-        Executor getExecutor() {
-            return mExecutor;
-        }
     }
 
     private ISharedConnectivityService mService;
@@ -192,7 +188,7 @@
     private final String mServicePackageName;
     private final String mIntentAction;
     private ServiceConnection mServiceConnection;
-    private final UserManager mUserManager;
+    private UserManager mUserManager;
 
     /**
      * Creates a new instance of {@link SharedConnectivityManager}.
@@ -320,19 +316,15 @@
 
     private void registerCallbackInternal(SharedConnectivityClientCallback callback,
             SharedConnectivityCallbackProxy proxy) {
-        proxy.getExecutor().execute(
-                () -> {
-                    try {
-                        mService.registerCallback(proxy);
-                        synchronized (mProxyDataLock) {
-                            mProxyMap.put(callback, proxy);
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Exception in registerCallback", e);
-                        callback.onRegisterCallbackFailed(e);
-                    }
-                }
-        );
+        try {
+            mService.registerCallback(proxy);
+            synchronized (mProxyDataLock) {
+                mProxyMap.put(callback, proxy);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in registerCallback", e);
+            callback.onRegisterCallbackFailed(e);
+        }
     }
 
     /**